Reactμ createRefλ₯Ό λ§μ€ν°νμ¬ λͺ λ Ήν DOM λ° μ»΄ν¬λνΈ μΈμ€ν΄μ€ μ‘°μμ μ 볡νμΈμ. ν΄λμ€ μ»΄ν¬λνΈμμ ν¬μ»€μ€, λ―Έλμ΄, μλνν° ν΅ν©μ μν΄ μΈμ μ΄λ»κ² ν¨κ³Όμ μΌλ‘ μ¬μ©νλμ§ μμ보μΈμ.
React createRef: μ»΄ν¬λνΈ λ° DOM μμ μ§μ μνΈμμ©μ μν μλ²½ κ°μ΄λ
κ΄λ²μνκ³ μ’ μ’ λ³΅μ‘ν νλ μΉ κ°λ° νκ²½μμ Reactλ μ¬μ©μ μΈν°νμ΄μ€λ₯Ό ꡬμΆνλ μ μΈμ μ κ·Ό λ°©μμΌλ‘ μΈν΄ μ§λ°°μ μΈ νμΌλ‘ λΆμνμ΅λλ€. μ΄ ν¨λ¬λ€μμ κ°λ°μκ° μ§μ μ μΈ DOM μ‘°μμ ν΅ν΄ μκ°μ μνλ₯Ό λ¬μ±νλ λ°©λ²μ μ²λ°©ν기보λ€λ, λ°μ΄ν°λ₯Ό κΈ°λ°μΌλ‘ UIκ° μ΄λ»κ² 보μ¬μΌ νλμ§λ₯Ό μ€λͺ νλλ‘ κΆμ₯ν©λλ€. μ΄λ¬ν μΆμνλ UI κ°λ°μ ν¬κ² λ¨μννμ¬ μ ν리μΌμ΄μ μ λ μμΈ‘ κ°λ₯νκ³ , μΆλ‘ νκΈ° μ¬μ°λ©°, κ³ μ±λ₯μΌλ‘ λ§λ€μμ΅λλ€.
κ·Έλ¬λ μ€μ μΉ μ ν리μΌμ΄μ
μ μΈκ³λ κ±°μ μμ ν μ μΈμ μ΄μ§ μμ΅λλ€. κΈ°λ³Έ DOM(Document Object Model) μμλ ν΄λμ€ μ»΄ν¬λνΈ μΈμ€ν΄μ€μμ μ§μ μ μΈ μνΈμμ©μ΄ νΈλ¦¬ν λΏλ§ μλλΌ μ λμ μΌλ‘ νμν νΉμ νκ³ λ μΌλ°μ μΈ μλ리μ€κ° μμ΅λλ€. Reactμ μ μΈμ νλ¦μμ λ²μ΄λλ μ΄λ¬ν "νμΆκ΅¬"λ refλ‘ μλ €μ Έ μμ΅λλ€. Reactκ° μ΄λ¬ν μ°Έμ‘°λ₯Ό μμ±νκ³ κ΄λ¦¬νκΈ° μν΄ μ 곡νλ λ€μν λ©μ»€λμ¦ μ€μμ React.createRef()λ νΉν ν΄λμ€ μ»΄ν¬λνΈλ‘ μμ
νλ κ°λ°μμκ² κ΄λ ¨λ κΈ°μ΄μ μΈ APIλ‘ μ리 μ‘κ³ μμ΅λλ€.
μ΄ ν¬κ΄μ μΈ κ°μ΄λλ React.createRef()λ₯Ό μ΄ν΄νκ³ , ꡬννλ©°, λ§μ€ν°νκΈ° μν κ²°μ μ μΈ μλ£κ° λλ κ²μ λͺ©νλ‘ ν©λλ€. μ°λ¦¬λ κ·Έκ²μ λͺ©μ μ λν μμΈν νꡬλ₯Ό μμνκ³ , ꡬ문과 μ€μ μ μ© μ¬λ‘λ₯Ό κΉμ΄ νκ³ λ€λ©°, λͺ¨λ² μ¬λ‘λ₯Ό μ‘°λͺ
νκ³ , λ€λ₯Έ ref κ΄λ¦¬ μ λ΅κ³Ό ꡬλ³ν κ²μ
λλ€. λΉμ μ΄ λͺ
λ Ήν μνΈμμ©μ λν μ΄ν΄λ₯Ό κ³΅κ³ ν νλ €λ μλ ¨λ React κ°λ°μμ΄λ , μ΄ μ€μν κ°λ
μ νμ
νλ €λ μ μ
κ°λ°μμ΄λ , μ΄ κΈμ νλ μ¬μ©μ κ²½νμ 볡μ‘ν μꡬλ₯Ό μ°μνκ² μ²λ¦¬νλ λ κ²¬κ³ νκ³ , μ±λ₯μ΄ λ°μ΄λλ©°, μ μΈκ³μ μΌλ‘ μ κ·Ό κ°λ₯ν React μ ν리μΌEμ΄μ
μ ꡬμΆν μ μλ μ§μμ μ 곡ν κ²μ
λλ€.
Reactμ Ref μ΄ν΄νκΈ°: μ μΈμ μΈκ³μ λͺ λ Ήμ μΈκ³ μ°κ²°νκΈ°
ν΅μ¬μ μΌλ‘ Reactλ μ μΈμ νλ‘κ·Έλλ° μ€νμΌμ μ§μ§ν©λλ€. λΉμ μ μ»΄ν¬λνΈ, κ·Έ μν, κ·Έλ¦¬κ³ μ΄λ»κ² λ λλ§λ μ§λ₯Ό μ μν©λλ€. κ·Έλ¬λ©΄ Reactκ° μ€μ λΈλΌμ°μ DOMμ ν¨μ¨μ μΌλ‘ μ λ°μ΄νΈνμ¬ μ μΈλ UIλ₯Ό λ°μν©λλ€. μ΄ μΆμν κ³μΈ΅μ μμ²λκ² κ°λ ₯νμ¬ κ°λ°μλ₯Ό μ§μ μ μΈ DOM μ‘°μμ 볡μ‘μ±κ³Ό μ±λ₯ μ νλ‘λΆν° 보νΈν©λλ€. μ΄κ²μ΄ React μ ν리μΌμ΄μ μ΄ μ’ μ’ λ§€μ° λΆλλ½κ³ λ°μμ μΌλ‘ λκ»΄μ§λ μ΄μ μ λλ€.
λ¨λ°©ν₯ λ°μ΄ν° νλ¦κ³Ό κ·Έ νκ³
Reactμ μν€ν μ² κ°μ μ λ¨λ°©ν₯ λ°μ΄ν° νλ¦μ μμ΅λλ€. λ°μ΄ν°λ λΆλͺ¨ μ»΄ν¬λνΈμμ μμμΌλ‘ propsλ₯Ό ν΅ν΄ μμΈ‘ κ°λ₯νκ² μλλ‘ νλ₯΄κ³ , μ»΄ν¬λνΈ λ΄μ μν λ³κ²½μ ν΄λΉ νμ νΈλ¦¬λ₯Ό ν΅ν΄ μ νλλ 리λ λλ§μ μ λ°ν©λλ€. μ΄ λͺ¨λΈμ μμΈ‘ κ°λ₯μ±μ λμ΄κ³ λλ²κΉ μ ν¨μ¬ μ½κ² λ§λλλ€. λ°μ΄ν°κ° μ΄λμ μμλκ³ UIμ μ΄λ»κ² μν₯μ λ―ΈμΉλμ§ νμ μ μ μκΈ° λλ¬Έμ λλ€. κ·Έλ¬λ λͺ¨λ μνΈμμ©μ΄ μ΄ νν₯μ λ°μ΄ν° νλ¦κ³Ό μλ²½νκ² μΌμΉνμ§λ μμ΅λλ€.
λ€μκ³Ό κ°μ μλ리μ€λ₯Ό κ³ λ €ν΄ λ³΄μΈμ:
- μ¬μ©μκ° νΌμΌλ‘ μ΄λν λ νλ‘κ·Έλλ° λ°©μμΌλ‘ μ λ ₯ νλμ ν¬μ»€μ€λ₯Ό λ§μΆλ κ²½μ°.
<video>μμμμplay()λλpause()λ©μλλ₯Ό νΈλ¦¬κ±°νλ κ²½μ°.- λ μ΄μμμ λμ μΌλ‘ μ‘°μ νκΈ° μν΄ λ λλ§λ
<div>μ μ νν ν½μ ν¬κΈ°λ₯Ό μΈ‘μ νλ κ²½μ°. - DOM 컨ν μ΄λμ λν μ§μ μ μΈ μ κ·Όμ κΈ°λνλ 볡μ‘ν μλνν° μλ°μ€ν¬λ¦½νΈ λΌμ΄λΈλ¬λ¦¬(μ: D3.jsμ κ°μ μ°¨νΈ λΌμ΄λΈλ¬λ¦¬ λλ μ§λ μκ°ν λꡬ)λ₯Ό ν΅ν©νλ κ²½μ°.
μ΄λ¬ν μμ μ λ³Έμ§μ μΌλ‘ λͺ λ Ήνμ λλ€. μ¦, λ¨μν μνλ μνλ₯Ό μ μΈνλ κ²μ΄ μλλΌ μμμκ² λ¬΄μΈκ°λ₯Ό νλλ‘ μ§μ λͺ λ Ήνλ κ²μ ν¬ν¨ν©λλ€. Reactμ μ μΈμ λͺ¨λΈμ μ’ μ’ λ§μ λͺ λ Ήν μΈλΆ μ¬νμ μΆμνν μ μμ§λ§, κ·Έ νμμ±μ μμ ν μ κ±°νμ§λ μμ΅λλ€. λ°λ‘ μ΄ μ§μ μμ refκ° λ±μ₯νμ¬ μ΄λ¬ν μ§μ μ μΈ μνΈμμ©μ μνν μ μλ ν΅μ λ νμΆκ΅¬λ₯Ό μ 곡ν©λλ€.
Refλ μΈμ μ¬μ©ν΄μΌ νλκ°: λͺ λ Ήν vs. μ μΈν μνΈμμ© νμνκΈ°
refλ₯Ό μ¬μ©ν λ κ°μ₯ μ€μν μμΉμ μ λμ μΌλ‘ νμν κ²½μ°μλ§ λλ¬Όκ² μ¬μ©νλ κ²μ λλ€. μ΄λ€ μμ μ΄ Reactμ νμ€ μ μΈμ λ©μ»€λμ¦(stateμ props)μ μ¬μ©νμ¬ λ¬μ±λ μ μλ€λ©΄, κ·Έκ²μ΄ νμ μ νΈλλ μ κ·Ό λ°©μμ΄μ΄μΌ ν©λλ€. refμ κ³Όλνκ² μμ‘΄νλ©΄ μ½λλ₯Ό μ΄ν΄νκ³ , μ μ§ κ΄λ¦¬νλ©°, λλ²κΉ νκΈ°κ° λ μ΄λ €μμ Έ Reactκ° μ 곡νλ λ°λ‘ κ·Έ μ΄μ μ νΌμν μ μμ΅λλ€.
κ·Έλ¬λ DOM λ Έλλ μ»΄ν¬λνΈ μΈμ€ν΄μ€μ λν μ§μ μ μΈ μ κ·Όμ΄ μ§μ μΌλ‘ νμν μν©μμλ refκ° μ¬λ°λ₯΄κ³ μλλ ν΄κ²°μ± μ λλ€. λ€μμ μ μ ν μ¬μ© μ¬λ‘μ λν λ μμΈν μ€λͺ μ λλ€:
- ν¬μ»€μ€, ν μ€νΈ μ ν, λ―Έλμ΄ μ¬μ κ΄λ¦¬: μ΄λ μμμ λͺ λ ΉνμΌλ‘ μνΈμμ©ν΄μΌ νλ μ νμ μΈ μμ λλ€. νμ΄μ§ λ‘λ μ κ²μμ°½μ μλ ν¬μ»€μ€λ₯Ό λ§μΆκ±°λ, μ λ ₯ νλμ λͺ¨λ ν μ€νΈλ₯Ό μ ννκ±°λ, μ€λμ€ λλ λΉλμ€ νλ μ΄μ΄μ μ¬μμ μ μ΄νλ κ²μ μκ°ν΄ 보μΈμ. μ΄λ¬ν μμ μ μΌλ°μ μΌλ‘ propsλ stateλ₯Ό λ³κ²½νλ κ²μ΄ μλλΌ μ¬μ©μ μ΄λ²€νΈλ μ»΄ν¬λνΈ μλͺ μ£ΌκΈ° λ©μλμ μν΄ νΈλ¦¬κ±°λ©λλ€.
- λͺ λ Ήν μ λλ©μ΄μ νΈλ¦¬κ±°: λ§μ μ λλ©μ΄μ μ΄ CSS μ ν/μ λλ©μ΄μ λλ React μ λλ©μ΄μ λΌμ΄λΈλ¬λ¦¬λ‘ μ μΈμ μΌλ‘ μ²λ¦¬λ μ μμ§λ§, νΉν HTML Canvas API, WebGLμ ν¬ν¨νκ±°λ Reactμ λ λλ§ μ£ΌκΈ° μΈλΆμμ κ΄λ¦¬νλ κ²μ΄ κ°μ₯ μ’μ μμ μμ±μ λν μΈλ°ν μ μ΄κ° νμν μΌλΆ 볡μ‘νκ³ κ³ μ±λ₯μ μ λλ©μ΄μ μ refλ₯Ό νμλ‘ ν μ μμ΅λλ€.
- μλνν° DOM λΌμ΄λΈλ¬λ¦¬μ ν΅ν©: λ§μ μ€λλ μλ°μ€ν¬λ¦½νΈ λΌμ΄λΈλ¬λ¦¬(μ: D3.js, μ§λμ© Leaflet, λ€μν λ κ±°μ UI ν΄ν·)λ νΉμ DOM μμλ₯Ό μ§μ μ‘°μνλλ‘ μ€κ³λμμ΅λλ€. Refλ νμμ μΈ λ€λ¦¬ μν μ νμ¬, Reactκ° μ»¨ν μ΄λ μμλ₯Ό λ λλ§ν λ€μ μλνν° λΌμ΄λΈλ¬λ¦¬κ° μ체 λͺ λ Ήν λ λλ§ λ‘μ§μ μν΄ ν΄λΉ 컨ν μ΄λμ μ κ·Όν μ μλλ‘ νμ©ν©λλ€.
-
μμ ν¬κΈ° λλ μμΉ μΈ‘μ : κ³ κΈ λ μ΄μμ, κ°μν λλ μ¬μ©μ μ μ μ€ν¬λ‘€ λμμ ꡬννλ €λ©΄ μμμ ν¬κΈ°, λ·°ν¬νΈμ λν μλμ μμΉ λλ μ€ν¬λ‘€ λμ΄μ λν μ νν μ λ³΄κ° μ’
μ’
νμν©λλ€.
getBoundingClientRect()μ κ°μ APIλ μ€μ DOM λ Έλμμλ§ μ κ·Όν μ μμΌλ―λ‘ μ΄λ¬ν κ³μ°μ refκ° νμμ μ λλ€.
λ°λλ‘, μ μΈμ μΌλ‘ λ¬μ±ν μ μλ μμ μλ ref μ¬μ©μ νΌν΄μΌ ν©λλ€. μ¬κΈ°μλ λ€μμ΄ ν¬ν¨λ©λλ€:
- μ»΄ν¬λνΈμ μ€νμΌ μμ (μ‘°κ±΄λΆ μ€νμΌλ§μλ state μ¬μ©).
- μμμ ν μ€νΈ μ½ν μΈ λ³κ²½ (propμΌλ‘ μ λ¬νκ±°λ state μ λ°μ΄νΈ).
- 볡μ‘ν μ»΄ν¬λνΈ ν΅μ (μΌλ°μ μΌλ‘ propsμ μ½λ°±μ΄ λ μ°μν¨).
- μν κ΄λ¦¬μ κΈ°λ₯μ 볡μ νλ €λ λͺ¨λ μλ리μ€.
React.createRef() κΉμ΄ νκ³ λ€κΈ°: ν΄λμ€ μ»΄ν¬λνΈλ₯Ό μν νλμ μ κ·Όλ²
React.createRef()λ React 16.3μ λμ
λμ΄, λ¬Έμμ΄ ref(νμ¬λ μ¬μ©λμ§ μμ)λ μ½λ°± ref(μ¬μ ν μ ν¨νμ§λ§ μ’
μ’
λ μ₯ν©ν¨)μ κ°μ μ€λλ λ°©λ²μ λΉν΄ refλ₯Ό κ΄λ¦¬νλ λ λͺ
μμ μ΄κ³ κΉλν λ°©λ²μ μ 곡ν©λλ€. μ΄κ²μ ν΄λμ€ μ»΄ν¬λνΈλ₯Ό μν μ£Όμ ref μμ± λ©μ»€λμ¦μΌλ‘ μ€κ³λμμΌλ©°, ν΄λμ€ κ΅¬μ‘° λ΄μμ μμ°μ€λ½κ² λ§λ κ°μ²΄ μ§ν₯ APIλ₯Ό μ 곡ν©λλ€.
ꡬ문 λ° κΈ°λ³Έ μ¬μ©λ²: 3λ¨κ³ κ³Όμ
createRef()λ₯Ό μ¬μ©νλ μν¬νλ‘λ κ°λ¨νλ©° μΈ κ°μ§ μ£Όμ λ¨κ³λ₯Ό ν¬ν¨ν©λλ€:
-
Ref κ°μ²΄ μμ±: ν΄λμ€ μ»΄ν¬λνΈμ μμ±μμμ
React.createRef()λ₯Ό νΈμΆνμ¬ ref μΈμ€ν΄μ€λ₯Ό μ΄κΈ°ννκ³ κ·Έ λ°ν κ°μ μΈμ€ν΄μ€ μμ±(μ:this.myRef)μ ν λΉν©λλ€. -
Ref μ°κ²°: μ»΄ν¬λνΈμ
renderλ©μλμμ, μ°Έμ‘°νλ €λ React μμ(HTML μμ λλ ν΄λμ€ μ»΄ν¬λνΈ)μrefμμ±μ μμ±λ ref κ°μ²΄λ₯Ό μ λ¬ν©λλ€. -
λμ μ κ·Ό: μ»΄ν¬λνΈκ° λ§μ΄νΈλλ©΄, μ°Έμ‘°λ DOM λ
Έλ λλ μ»΄ν¬λνΈ μΈμ€ν΄μ€λ ref κ°μ²΄μ
.currentμμ±μ ν΅ν΄ μ¬μ©ν μ μμ΅λλ€(μ:this.myRef.current).
import React from 'react';
class FocusInputOnMount extends React.Component {
constructor(props) {
super(props);
this.inputElementRef = React.createRef(); // 1λ¨κ³: μμ±μμμ ref κ°μ²΄ μμ±
console.log('Constructor: Ref current κ°μ μ΄κΈ°μ:', this.inputElementRef.current); // null
}
componentDidMount() {
if (this.inputElementRef.current) {
this.inputElementRef.current.focus();
console.log('ComponentDidMount: μ
λ ₯μ°½μ ν¬μ»€μ€λ¨. νμ¬ κ°:', this.inputElementRef.current.value);
}
}
handleButtonClick = () => {
if (this.inputElementRef.current) {
alert(`μ
λ ₯κ°: ${this.inputElementRef.current.value}`);
}
};
render() {
console.log('Render: Ref current κ°μ:', this.inputElementRef.current); // μ΄κΈ° λ λλ§ μ μ¬μ ν null
return (
<div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
<h3>μλ ν¬μ»€μ€ μ
λ ₯ νλ</h3>
<label htmlFor="focusInput">μ΄λ¦μ μ
λ ₯νμΈμ:</label><br />
<input
id="focusInput"
type="text"
ref={this.inputElementRef} // 2λ¨κ³: <input> μμμ ref μ°κ²°
placeholder="μ΄λ¦μ μ¬κΈ°μ..."
style={{ margin: '10px 0', padding: '8px', borderRadius: '4px', border: '1px solid #ddd' }}
/><br />
<button
onClick={this.handleButtonClick}
style={{ padding: '10px 15px', background: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
μ
λ ₯κ° νμ
</button>
<p><em>μ΄ μ
λ ₯μ°½μ μ»΄ν¬λνΈκ° λ‘λλ λ μλμΌλ‘ ν¬μ»€μ€λ₯Ό λ°μ΅λλ€.</em></p>
</div>
);
}
}
μ΄ μμμ this.inputElementRefλ Reactκ° λ΄λΆμ μΌλ‘ κ΄λ¦¬νλ κ°μ²΄μ
λλ€. <input> μμκ° λ λλ§λκ³ DOMμ λ§μ΄νΈλ λ, Reactλ ν΄λΉ μ€μ DOM λ
Έλλ₯Ό this.inputElementRef.currentμ ν λΉν©λλ€. componentDidMount μλͺ
μ£ΌκΈ° λ©μλλ μ»΄ν¬λνΈμ κ·Έ μμλ€μ΄ DOMμ λ λλ§λμκ³ .current μμ±μ΄ μ¬μ© κ°λ₯νλ©° μ±μμ Έ μμμ 보μ₯νκΈ° λλ¬Έμ refμ μνΈμμ©νκΈ°μ μ΄μμ μΈ μ₯μμ
λλ€.
DOM μμμ Ref μ°κ²°νκΈ°: μ§μ μ μΈ DOM μ κ·Ό
νμ€ HTML μμ(μ: <div>, <p>, <button>, <img>)μ refλ₯Ό μ°κ²°νλ©΄, ref κ°μ²΄μ .current μμ±μ μ€μ κΈ°λ³Έ DOM μμλ₯Ό 보μ νκ² λ©λλ€. μ΄κ²μ λͺ¨λ νμ€ λΈλΌμ°μ DOM APIμ λν μ ν μλ μ κ·Όμ μ 곡νμ¬, μΌλ°μ μΌλ‘ Reactμ μ μΈμ μ μ΄ λ²μλ₯Ό λ²μ΄λλ μμ
μ μνν μ μκ² ν©λλ€. μ΄λ λ€μν μ¬μ©μ νκ²½κ³Ό μ₯μΉ μ νμ κ±Έμ³ μ νν λ μ΄μμ, μ€ν¬λ‘€λ§ λλ ν¬μ»€μ€ κ΄λ¦¬κ° μ€μν μ μλ κΈλ‘λ² μ ν리μΌμ΄μ
μ νΉν μ μ©ν©λλ€.
import React from 'react';
class ScrollToElementExample extends React.Component {
constructor(props) {
super(props);
this.targetDivRef = React.createRef();
this.state = { showScrollButton: false };
}
componentDidMount() {
// μ€ν¬λ‘€ν λ§νΌ μΆ©λΆν μ½ν
μΈ κ° μλ κ²½μ°μλ§ μ€ν¬λ‘€ λ²νΌ νμ
// μ΄ νμΈμ refκ° μ΄λ―Έ current μνμμ 보μ₯ν©λλ€.
if (this.targetDivRef.current && window.innerHeight < document.body.scrollHeight) {
this.setState({ showScrollButton: true });
}
}
handleScrollToTarget = () => {
if (this.targetDivRef.current) {
// μ μΈκ³μ μΌλ‘ λΈλΌμ°μ μμ λ리 μ§μλλ λΆλλ¬μ΄ μ€ν¬λ‘€λ§μ μν΄ scrollIntoView μ¬μ©.
this.targetDivRef.current.scrollIntoView({
behavior: 'smooth', // λ λμ μ¬μ©μ κ²½νμ μν΄ μ€ν¬λ‘€μ μ λλ©μ΄μ
ν¨κ³Ό μ μ©
block: 'start' // μμμ μλ¨μ λ·°ν¬νΈμ μλ¨μ λ§μΆ€
});
console.log('λμ divλ‘ μ€ν¬λ‘€λ¨!');
} else {
console.warn('μ€ν¬λ‘€ν λμ divκ° μμ§ μ¬μ© κ°λ₯νμ§ μμ΅λλ€.');
}
};
render() {
return (
<div style={{ padding: '15px' }}>
<h2>Refλ‘ νΉμ μμλ‘ μ€ν¬λ‘€νκΈ°</h2>
<p>μ΄ μμ λ νλ©΄ λ°μ μλ DOM μμλ‘ νλ‘κ·Έλλ° λ°©μμΌλ‘ μ€ν¬λ‘€νλ λ°©λ²μ 보μ¬μ€λλ€.</p>
{this.state.showScrollButton && (
<button
onClick={this.handleScrollToTarget}
style={{ marginBottom: '20px', padding: '10px 20px', background: '#28a745', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
λμ μμμΌλ‘ μ€ν¬λ‘€ λ€μ΄
</button>
)}
<div style={{ height: '1500px', background: '#f8f9fa', padding: '20px', marginBottom: '20px', border: '1px dashed #6c757d' }}>
<p>μμ§ μ€ν¬λ‘€ 곡κ°μ λ§λ€κΈ° μν νλ μ΄μ€νλ μ½ν
μΈ μ
λλ€.</p>
<p>μ¬μ©μκ° λ°©λν μ½ν
μΈ λ₯Ό νμν΄μΌ νλ κΈ΄ κΈ°μ¬, 볡μ‘ν νΌ λλ μμΈν λμ보λλ₯Ό μμν΄ λ³΄μΈμ. νλ‘κ·Έλλ° λ°©μμ μ€ν¬λ‘€λ§μ μ¬μ©μκ° μλμ μΈ λ
Έλ ₯ μμ΄ κ΄λ ¨ μΉμ
μ λΉ λ₯΄κ² λλ¬ν μ μλλ‘ λ³΄μ₯νμ¬ λͺ¨λ μ₯μΉμ νλ©΄ ν¬κΈ°μμ μ κ·Όμ±κ³Ό μ¬μ©μ νλ¦μ κ°μ ν©λλ€.</p>
<p>μ΄ κΈ°μ μ λ€μ€ νμ΄μ§ νΌ, λ¨κ³λ³ λ§λ²μ¬ λλ κΉμ νμμ΄ μλ λ¨μΌ νμ΄μ§ μ ν리μΌμ΄μ
μμ νΉν μ μ©ν©λλ€.</p>
</div>
<div
ref={this.targetDivRef} // μ¬κΈ°μ ref μ°κ²°
style={{
minHeight: '300px',
background: '#e9ecef',
padding: '30px',
border: '2px solid #007bff',
borderRadius: '10px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
textAlign: 'center'
}}
>
<h3>λμ μμμ λλ¬νμ΅λλ€!</h3>
<p>μ΄κ³³μ΄ μ°λ¦¬κ° νλ‘κ·Έλλ° λ°©μμΌλ‘ μ€ν¬λ‘€ν μΉμ
μ
λλ€.</p>
<p>μ€ν¬λ‘€ λμμ μ λ°νκ² μ μ΄νλ λ₯λ ₯μ μ¬μ©μ κ²½νμ ν₯μμν€λ λ° μ€μνλ©°, νΉν νλ©΄ 곡κ°μ΄ μ νμ μ΄κ³ μ λ°ν νμμ΄ κ°μ₯ μ€μν λͺ¨λ°μΌ μ₯μΉμμ κ·Έλ μ΅λλ€.</p>
</div>
</div>
);
}
}
μ΄ μμ λ createRefκ° λΈλΌμ°μ μμ€μ μνΈμμ©μ λν μ μ΄λ₯Ό μ΄λ»κ² μ 곡νλμ§ μλ¦λ΅κ² 보μ¬μ€λλ€. μ΄λ¬ν νλ‘κ·Έλλ° λ°©μμ μ€ν¬λ‘€ κΈ°λ₯μ κΈ΄ λ¬Έμλ₯Ό νμνλ κ²λΆν° 볡μ‘ν μν¬νλ‘λ₯Ό ν΅ν΄ μ¬μ©μλ₯Ό μλ΄νλ κ²κΉμ§ λ§μ μ ν리μΌμ΄μ
μμ μ€μν©λλ€. scrollIntoViewμ behavior: 'smooth' μ΅μ
μ μ¦κ±°μ΄ μ λλ©μ΄μ
μ νμ 보μ₯νμ¬ μ μΈκ³μ μΌλ‘ μ¬μ©μ κ²½νμ ν₯μμν΅λλ€.
ν΄λμ€ μ»΄ν¬λνΈμ Ref μ°κ²°νκΈ°: μΈμ€ν΄μ€μ μνΈμμ©
λ€μ΄ν°λΈ DOM μμ μΈμλ, ν΄λμ€ μ»΄ν¬λνΈμ μΈμ€ν΄μ€μ refλ₯Ό μ°κ²°ν μλ μμ΅λλ€. μ΄λ κ² νλ©΄ ref κ°μ²΄μ .current μμ±μ μ€μ μΈμ€ν΄μ€νλ ν΄λμ€ μ»΄ν¬λνΈ μ체λ₯Ό 보μ νκ² λ©λλ€. μ΄λ₯Ό ν΅ν΄ λΆλͺ¨ μ»΄ν¬λνΈλ μμ ν΄λμ€ μ»΄ν¬λνΈ λ΄μ μ μλ λ©μλλ₯Ό μ§μ νΈμΆνκ±°λ μΈμ€ν΄μ€ μμ±μ μ κ·Όν μ μμ΅λλ€. κ°λ ₯νμ§λ§, μ΄ κΈ°λ₯μ μ ν΅μ μΈ λ¨λ°©ν₯ λ°μ΄ν° νλ¦μ κΉ¨λ¨λ¦΄ μ μμ΄ μ μ¬μ μΌλ‘ μμΈ‘νκΈ° μ΄λ €μ΄ μ ν리μΌμ΄μ
λμμΌλ‘ μ΄μ΄μ§ μ μμΌλ―λ‘ κ·Ήλμ μ£Όμλ₯Ό κΈ°μΈμ¬ μ¬μ©ν΄μΌ ν©λλ€.
import React from 'react';
// μμ ν΄λμ€ μ»΄ν¬λνΈ
class DialogBox extends React.Component {
constructor(props) {
super(props);
this.state = { isOpen: false, message: '' };
}
// refλ₯Ό ν΅ν΄ λΆλͺ¨μκ² λ
ΈμΆλλ λ©μλ
open(message) {
this.setState({ isOpen: true, message });
}
close = () => {
this.setState({ isOpen: false, message: '' });
};
render() {
if (!this.state.isOpen) return null;
return (
<div style={{
position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)',
padding: '25px 35px', background: 'white', border: '1px solid #ddd', borderRadius: '8px',
boxShadow: '0 5px 15px rgba(0,0,0,0.2)', zIndex: 1000, maxWidth: '400px', width: '90%', textAlign: 'center'
}}>
<h4>λΆλͺ¨λ‘λΆν°μ λ©μμ§</h4>
<p>{this.state.message}</p>
<button
onClick={this.close}
style={{ marginTop: '15px', padding: '8px 15px', background: '#dc3545', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
λ«κΈ°
</button>
</div>
);
}
}
// λΆλͺ¨ ν΄λμ€ μ»΄ν¬λνΈ
class AppWithDialog extends React.Component {
constructor(props) {
super(props);
this.dialogRef = React.createRef();
}
handleOpenDialog = () => {
if (this.dialogRef.current) {
// μμ μ»΄ν¬λνΈ μΈμ€ν΄μ€μ μ κ·Όνμ¬ 'open' λ©μλλ₯Ό νΈμΆ
this.dialogRef.current.open('μλ
νμΈμ, λΆλͺ¨ μ»΄ν¬λνΈμ
λλ€! μ΄ λ€μ΄μΌλ‘κ·Έλ λͺ
λ Ήμ μΌλ‘ μ΄λ Έμ΅λλ€.');
}
};
render() {
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h2>Refλ₯Ό ν΅ν λΆλͺ¨-μμ ν΅μ </h2>
<p>μ΄κ²μ λΆλͺ¨ μ»΄ν¬λνΈκ° μμ ν΄λμ€ μ»΄ν¬λνΈμ λ©μλλ₯Ό λͺ
λ Ήμ μΌλ‘ μ μ΄νλ λ°©λ²μ 보μ¬μ€λλ€.</p>
<button
onClick={this.handleOpenDialog}
style={{ padding: '12px 25px', background: '#007bff', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', fontSize: '1.1em' }}
>
λͺ
λ Ήν λ€μ΄μΌλ‘κ·Έ μ΄κΈ°
</button>
<DialogBox ref={this.dialogRef} /> // ν΄λμ€ μ»΄ν¬λνΈ μΈμ€ν΄μ€μ ref μ°κ²°
</div>
);
}
}
μ¬κΈ°μ AppWithDialogλ refλ₯Ό ν΅ν΄ DialogBox μ»΄ν¬λνΈμ open λ©μλλ₯Ό μ§μ νΈμΆν μ μμ΅λλ€. μ΄ ν¨ν΄μ λͺ¨λ¬ νμ, νΌ μ¬μ€μ λλ μμ μ»΄ν¬λνΈ λ΄μ μΊ‘μνλ μΈλΆ UI μμλ₯Ό νλ‘κ·Έλλ° λ°©μμΌλ‘ μ μ΄νλ κ²κ³Ό κ°μ μμ
μ νΈλ¦¬κ±°νλ λ° μ μ©ν μ μμ΅λλ€. κ·Έλ¬λ λλΆλΆμ μλ리μ€μμλ λͺ
ννκ³ μμΈ‘ κ°λ₯ν λ°μ΄ν° νλ¦μ μ μ§νκΈ° μν΄ λΆλͺ¨μμ μμμΌλ‘ λ°μ΄ν°μ μ½λ°±μ μ λ¬νλ prop κΈ°λ° ν΅μ μ μ νΈνλ κ²μ΄ μΌλ°μ μΌλ‘ κΆμ₯λ©λλ€. μμ μ»΄ν¬λνΈ λ©μλμ λν refλ ν΄λΉ μμ
μ΄ μ§μ μΌλ‘ λͺ
λ Ήμ μ΄κ³ μΌλ°μ μΈ prop/state νλ¦μ λ§μ§ μμ λλ§ μ¬μ©ν΄μΌ ν©λλ€.
ν¨μν μ»΄ν¬λνΈμ Ref μ°κ²°νκΈ° (μ€μν μ°¨μ΄μ )
createRef()λ₯Ό μ¬μ©νμ¬ ν¨μν μ»΄ν¬λνΈμ μ§μ refλ₯Ό μ°κ²°ν μ μλ€λ κ²μ νν μ€ν΄μ΄μ μ€μν ꡬλ³μ μ
λλ€. ν¨μν μ»΄ν¬λνΈλ λ³Έμ§μ μΌλ‘ ν΄λμ€ μ»΄ν¬λνΈμ κ°μ λ°©μμΌλ‘ μΈμ€ν΄μ€λ₯Ό κ°μ§ μμ΅λλ€. ν¨μν μ»΄ν¬λνΈμ μ§μ refλ₯Ό ν λΉνλ €κ³ νλ©΄ (μ: <MyFunctionalComponent ref={this.myRef} />), Reactλ κ°λ° λͺ¨λμμ κ²½κ³ λ₯Ό λ°μμν΅λλ€. μλνλ©΄ .currentμ ν λΉν μ»΄ν¬λνΈ μΈμ€ν΄μ€κ° μκΈ° λλ¬Έμ
λλ€.
λ§μ½ λΆλͺ¨ μ»΄ν¬λνΈ(createRefλ₯Ό μ¬μ©νλ ν΄λμ€ μ»΄ν¬λνΈμΌ μλ μκ³ , useRefλ₯Ό μ¬μ©νλ ν¨μν μ»΄ν¬λνΈμΌ μλ μμ)κ° ν¨μν μμ μ»΄ν¬λνΈ λ΄λΆμμ λ λλ§λ DOM μμμ μ κ·Όν μ μλλ‘ νλ κ²μ΄ λͺ©νλΌλ©΄, React.forwardRefλ₯Ό μ¬μ©ν΄μΌ ν©λλ€. μ΄ κ³ μ°¨ μ»΄ν¬λνΈλ ν¨μν μ»΄ν¬λνΈκ° νΉμ DOM λ
Έλλ μμ λ΄λΆμ λͺ
λ Ήν νΈλ€μ λν refλ₯Ό λ
ΈμΆν μ μκ² ν΄μ€λλ€.
λλ, ν¨μν μ»΄ν¬λνΈ λ΄μμ μμ
νλ©΄μ refλ₯Ό μμ±νκ³ κ΄λ¦¬ν΄μΌ νλ€λ©΄, μ μ ν λ©μ»€λμ¦μ useRef ν
μ΄λ©°, μ΄λ λμ€μ λΉκ΅ μΉμ
μμ κ°λ΅νκ² λ
Όμλ κ²μ
λλ€. createRefλ κ·Όλ³Έμ μΌλ‘ ν΄λμ€ μ»΄ν¬λνΈμ κ·Έ μΈμ€ν΄μ€ κΈ°λ° νΉμ±μ λ¬Άμ¬ μλ€λ μ μ κΈ°μ΅νλ κ²μ΄ μ€μν©λλ€.
DOM λ Έλ λλ μ»΄ν¬λνΈ μΈμ€ν΄μ€ μ κ·ΌνκΈ°: `.current` μμ± μ€λͺ
ref μνΈμμ©μ ν΅μ¬μ React.createRef()μ μν΄ μμ±λ ref κ°μ²΄μ .current μμ±μ μ€μ¬μΌλ‘ μ΄λ£¨μ΄μ§λλ€. κ·Έ μλͺ
μ£ΌκΈ°μ 무μμ λ΄μ μ μλμ§ μ΄ν΄νλ κ²μ ν¨κ³Όμ μΈ ref κ΄λ¦¬μ κ°μ₯ μ€μν©λλ€.
`.current` μμ±: λͺ λ Ήν μ μ΄λ‘μ κ΄λ¬Έ
.current μμ±μ Reactκ° κ΄λ¦¬νλ λ³κ²½ κ°λ₯ν κ°μ²΄μ
λλ€. μ΄κ²μ μ°Έμ‘°λ μμλ μ»΄ν¬λνΈ μΈμ€ν΄μ€μ λν μ§μ μ μΈ λ§ν¬ μν μ ν©λλ€. κ·Έ κ°μ μ»΄ν¬λνΈμ μλͺ
μ£ΌκΈ° λμ λ³κ²½λ©λλ€:
-
μ΄κΈ°ν: μμ±μμμ μ²μ
React.createRef()λ₯Ό νΈμΆνλ©΄, ref κ°μ²΄κ° μμ±λκ³ κ·Έ.currentμμ±μnullλ‘ μ΄κΈ°νλ©λλ€. μ΄λ μ΄ λ¨κ³μμλ μ»΄ν¬λνΈκ° μμ§ λ λλ§λμ§ μμκ³ , refκ° κ°λ¦¬ν¬ DOM μμλ μ»΄ν¬λνΈ μΈμ€ν΄μ€κ° μ‘΄μ¬νμ§ μκΈ° λλ¬Έμ λλ€. -
λ§μ΄ν
: μ»΄ν¬λνΈκ° DOMμ λ λλ§λκ³
refμμ±μ κ°μ§ μμκ° μμ±λλ©΄, Reactλ μ€μ DOM λ Έλλ ν΄λμ€ μ»΄ν¬λνΈ μΈμ€ν΄μ€λ₯Ό ref κ°μ²΄μ.currentμμ±μ ν λΉν©λλ€. μ΄λ μΌλ°μ μΌλ‘renderλ©μλκ° μλ£λ μ§νμcomponentDidMountκ° νΈμΆλκΈ° μ μ λ°μν©λλ€. λ°λΌμ,componentDidMountλ.currentμ μ κ·Όνκ³ μνΈμμ©νκΈ°μ κ°μ₯ μμ νκ³ μΌλ°μ μΈ μ₯μμ λλ€. -
μΈλ§μ΄ν
: μ»΄ν¬λνΈκ° DOMμμ μΈλ§μ΄νΈλλ©΄, Reactλ μλμΌλ‘
.currentμμ±μ λ€μnullλ‘ μ¬μ€μ ν©λλ€. μ΄λ λ©λͺ¨λ¦¬ λμλ₯Ό λ°©μ§νκ³ μ ν리μΌμ΄μ μ΄ λ μ΄μ DOMμ μ‘΄μ¬νμ§ μλ μμμ λν μ°Έμ‘°λ₯Ό μ μ§νμ§ μλλ‘ λ³΄μ₯νλ λ° μ€μν©λλ€. -
μ
λ°μ΄νΈ: λλ¬Όκ² μ
λ°μ΄νΈ μ€μ μμμ
refμμ±μ΄ λ³κ²½λλ κ²½μ°, μ΄μ refμcurrentμμ±μ μ refμcurrentμμ±μ΄ μ€μ λκΈ° μ μnullλ‘ μ€μ λ©λλ€. μ΄ λμμ λ μΌλ°μ μ΄μ§λ§ 볡μ‘ν λμ ref ν λΉμ λν΄ μμλλ κ²μ΄ μ€μν©λλ€.
import React from 'react';
class RefLifecycleLogger extends React.Component {
constructor(props) {
super(props);
this.myDivRef = React.createRef();
console.log('1. Constructor: this.myDivRef.current is', this.myDivRef.current); // null
}
componentDidMount() {
console.log('3. componentDidMount: this.myDivRef.current is', this.myDivRef.current); // μ€μ DOM μμ
if (this.myDivRef.current) {
this.myDivRef.current.style.backgroundColor = '#d4edda'; // μμ°μ μν λͺ
λ Ήν μ€νμΌλ§
this.myDivRef.current.innerText += ' - Refκ° νμ±νλμμ΅λλ€!';
}
}
componentDidUpdate(prevProps, prevState) {
console.log('4. componentDidUpdate: this.myDivRef.current is', this.myDivRef.current); // μ€μ DOM μμ (μ
λ°μ΄νΈ ν)
}
componentWillUnmount() {
console.log('5. componentWillUnmount: this.myDivRef.current is', this.myDivRef.current); // μ€μ DOM μμ (nullλ‘ μ€μ λκΈ° μ§μ )
// μ΄ μμ μμ νμν κ²½μ° μ 리 μμ
μ μνν μ μμ΅λλ€.
}
render() {
// μ΄κΈ° λ λλ§ μ, DOMμ΄ μμ§ μμ±λμ§ μμκΈ° λλ¬Έμ this.myDivRef.currentλ μ¬μ ν nullμ
λλ€.
// νμ λ λλ§(λ§μ΄νΈ ν)μμλ μμλ₯Ό 보μ νκ² λ©λλ€.
console.log('2. Render: this.myDivRef.current is', this.myDivRef.current);
return (
<div
ref={this.myDivRef}
style={{ padding: '20px', border: '1px solid #28a745', margin: '20px', minHeight: '80px', display: 'flex', alignItems: 'center' }}
>
<p>μ΄κ²μ refκ° μ°κ²°λ divμ
λλ€.</p>
</div>
);
}
}
RefLifecycleLoggerμ μ½μ μΆλ ₯μ κ΄μ°°νλ©΄ this.myDivRef.currentκ° μΈμ μ¬μ© κ°λ₯ν΄μ§λμ§μ λν λͺ
νν ν΅μ°°λ ₯μ μ»μ μ μμ΅λλ€. νΉν λ§μ΄ν
μ μ΄λ μΈλ§μ΄ν
νμ μ€νλ μ μλ λ©μλμμ μνΈμμ©μ μλνκΈ° μ μ this.myDivRef.currentκ° nullμ΄ μλμ§ νμ νμΈνλ κ²μ΄ μ€μν©λλ€.
`.current`λ 무μμ λ΄μ μ μλκ°? Refμ λ΄μ© νμνκΈ°
currentκ° λ³΄μ νλ κ°μ μ νμ refλ₯Ό 무μμ μ°κ²°νλμ§μ λ°λΌ λ¬λΌμ§λλ€:
-
HTML μμ(μ:
<div>,<input>)μ μ°κ²°λ κ²½μ°:.currentμμ±μ μ€μ κΈ°λ³Έ DOM μμλ₯Ό ν¬ν¨ν©λλ€. μ΄κ²μ λ€μ΄ν°λΈ μλ°μ€ν¬λ¦½νΈ κ°μ²΄μ΄λ©°, μ 체 DOM API λ²μμ μ κ·Όν μ μκ² ν΄μ€λλ€. μλ₯Ό λ€μ΄,<input type="text">μ refλ₯Ό μ°κ²°νλ©΄,.currentλHTMLInputElementκ°μ²΄κ° λμ΄.focus()μ κ°μ λ©μλλ₯Ό νΈμΆνκ±°λ,.valueμ κ°μ μμ±μ μ½κ±°λ,.placeholderμ κ°μ μμ±μ μμ ν μ μμ΅λλ€. μ΄κ²μ΄ refμ κ°μ₯ μΌλ°μ μΈ μ¬μ© μ¬λ‘μ λλ€.this.inputRef.current.focus();
this.videoRef.current.play();
const { width, height } = this.divRef.current.getBoundingClientRect(); -
ν΄λμ€ μ»΄ν¬λνΈ(μ:
<MyClassComponent />)μ μ°κ²°λ κ²½μ°:.currentμμ±μ ν΄λΉ ν΄λμ€ μ»΄ν¬λνΈμ μΈμ€ν΄μ€λ₯Ό 보μ ν©λλ€. μ΄λ ν΄λΉ μμ μ»΄ν¬λνΈ λ΄μ μ μλ λ©μλλ₯Ό μ§μ νΈμΆνκ±°λ(μ:childRef.current.someMethod()) μ¬μ§μ΄ κ·Έ stateλ propsμ μ κ·Όν μλ μμμ μλ―Έν©λλ€(λΉλ‘ refλ₯Ό ν΅ν΄ μμμ state/propsμ μ§μ μ κ·Όνλ κ²μ propsμ state μ λ°μ΄νΈλ₯Ό μ νΈνλ κ²λ³΄λ€ μΌλ°μ μΌλ‘ κΆμ₯λμ§ μμ§λ§). μ΄ κΈ°λ₯μ νμ€ prop κΈ°λ° μνΈμμ© λͺ¨λΈμ λ§μ§ μλ μμ μ»΄ν¬λνΈμ νΉμ λμμ νΈλ¦¬κ±°νλ λ° κ°λ ₯ν©λλ€.this.childComponentRef.current.resetForm();
// λλ¬Όμ§λ§ κ°λ₯: console.log(this.childComponentRef.current.state.someValue); -
ν¨μν μ»΄ν¬λνΈ(
forwardRefλ₯Ό ν΅ν΄)μ μ°κ²°λ κ²½μ°: μ΄μ μ μΈκΈνλ―μ΄, refλ ν¨μν μ»΄ν¬λνΈμ μ§μ μ°κ²°ν μ μμ΅λλ€. κ·Έλ¬λ ν¨μν μ»΄ν¬λνΈκ°React.forwardRefλ‘ λνλ κ²½μ°,.currentμμ±μ ν¨μν μ»΄ν¬λνΈκ° μ λ¬λ refλ₯Ό ν΅ν΄ λͺ μμ μΌλ‘ λ ΈμΆνλ κ°μ 보μ ν©λλ€. μ΄κ²μ μΌλ°μ μΌλ‘ ν¨μν μ»΄ν¬λνΈ λ΄μ DOM μμμ΄κ±°λ, λͺ λ Ήν λ©μλλ₯Ό ν¬ν¨νλ κ°μ²΄μ λλ€(forwardRefμ ν¨κ»useImperativeHandleν μ¬μ©).// λΆλͺ¨μμ myForwardedRef.currentλ λ ΈμΆλ DOM λ Έλ λλ κ°μ²΄κ° λ©λλ€.
this.myForwardedRef.current.focus();
this.myForwardedRef.current.customResetMethod();
`createRef`μ μ€μ μ¬μ© μ¬λ‘
React.createRef()μ μ μ©μ±μ μ§μ μΌλ‘ νμ
νκΈ° μν΄, λ¨μν ν¬μ»€μ€ κ΄λ¦¬λ₯Ό λμ΄ κ·Έκ²μ΄ νμμ μΈ κ²μΌλ‘ μ
μ¦λλ λ μμΈνκ³ μ μΈκ³μ μΌλ‘ κ΄λ ¨λ μλ리μ€λ₯Ό νμν΄ λ³΄κ² μ΅λλ€.
1. μ¬λ¬ λ¬ΈνκΆμ κ±Έμ³ ν¬μ»€μ€, ν μ€νΈ μ ν λλ λ―Έλμ΄ μ¬μ κ΄λ¦¬
μ΄κ²λ€μ λͺ λ Ήν UI μνΈμμ©μ μ£Όμ μμ λλ€. μ μΈκ³ μ¬μ©μλ₯Ό μν΄ μ€κ³λ λ€λ¨κ³ μμμ μμν΄ λ³΄μΈμ. μ¬μ©μκ° ν μΉμ μ μλ£ν ν, μΈμ΄λ κΈ°λ³Έ ν μ€νΈ λ°©ν₯(μΌμͺ½μμ μ€λ₯Έμͺ½ λλ μ€λ₯Έμͺ½μμ μΌμͺ½)μ κ΄κ³μμ΄ λ€μ μΉμ μ 첫 λ²μ§Έ μ λ ₯μΌλ‘ μλμΌλ‘ ν¬μ»€μ€λ₯Ό μ΄λμν€κ³ μΆμ μ μμ΅λλ€. Refλ νμν μ μ΄λ₯Ό μ 곡ν©λλ€.
import React from 'react';
class DynamicFocusForm extends React.Component {
constructor(props) {
super(props);
this.firstNameRef = React.createRef();
this.lastNameRef = React.createRef();
this.emailRef = React.createRef();
this.state = { currentStep: 1 };
}
componentDidMount() {
// μ»΄ν¬λνΈκ° λ§μ΄νΈλ λ 첫 λ²μ§Έ μ
λ ₯μ ν¬μ»€μ€
this.firstNameRef.current.focus();
}
handleNextStep = (nextRef) => {
this.setState(prevState => ({ currentStep: prevState.currentStep + 1 }), () => {
// μνκ° μ
λ°μ΄νΈλκ³ μ»΄ν¬λνΈκ° 리λ λλ§λ ν, λ€μ μ
λ ₯μ ν¬μ»€μ€
if (nextRef.current) {
nextRef.current.focus();
}
});
};
render() {
const { currentStep } = this.state;
const formSectionStyle = { border: '1px solid #0056b3', padding: '20px', margin: '15px 0', borderRadius: '8px', background: '#e7f0fa' };
const inputStyle = { width: '100%', padding: '10px', margin: '8px 0', border: '1px solid #ccc', borderRadius: '4px' };
const buttonStyle = { padding: '10px 20px', background: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginTop: '10px' };
return (
<div style={{ maxWidth: '600px', margin: '30px auto', padding: '25px', boxShadow: '0 4px 12px rgba(0,0,0,0.1)', borderRadius: '10px', background: 'white' }}>
<h2>Refλ‘ κ΄λ¦¬λλ ν¬μ»€μ€κ° μλ λ€λ¨κ³ μμ</h2>
<p>νμ¬ λ¨κ³: <strong>{currentStep}</strong></p>
{currentStep === 1 && (
<div style={formSectionStyle}>
<h3>κ°μΈ μ 보</h3>
<label htmlFor="firstName">μ΄λ¦:</label>
<input id="firstName" type="text" ref={this.firstNameRef} style={inputStyle} placeholder="μ: νκΈΈλ" />
<label htmlFor="lastName">μ±:</label>
<input id="lastName" type="text" ref={this.lastNameRef} style={inputStyle} placeholder="μ: ν" />
<button onClick={() => this.handleNextStep(this.emailRef)} style={buttonStyle}>λ€μ →</button>
</div>
)}
{currentStep === 2 && (
<div style={formSectionStyle}>
<h3>μ°λ½μ² μ 보</h3>
<label htmlFor="email">μ΄λ©μΌ:</label>
<input id="email" type="email" ref={this.emailRef} style={inputStyle} placeholder="μ: john.doe@example.com" />
<p>... κΈ°ν μ°λ½μ² νλ ...</p>
<button onClick={() => alert('μμμ΄ μ μΆλμμ΅λλ€!')} style={buttonStyle}>μ μΆ</button>
</div>
)}
<p><em>μ΄ μνΈμμ©μ νΉν ν€λ³΄λ νμμ΄λ 보쑰 κΈ°μ μ μμ‘΄νλ μ μΈκ³ μ¬μ©μμ μ κ·Όμ±κ³Ό μ¬μ©μ κ²½νμ ν¬κ² ν₯μμν΅λλ€.</em></p>
</div>
);
}
}
μ΄ μμ λ createRefλ₯Ό μ¬μ©νμ¬ νλ‘κ·Έλλ° λ°©μμΌλ‘ ν¬μ»€μ€λ₯Ό κ΄λ¦¬νλ μ€μ©μ μΈ λ€λ¨κ³ μμμ 보μ¬μ€λλ€. μ΄λ λ€μν μΈμ΄ λ° λ¬Ένμ λ§₯λ½μμ μ¬μ©λλ μ ν리μΌμ΄μ
μ λν μ€μν κ³ λ € μ¬νμΈ λΆλλ½κ³ μ κ·Ό κ°λ₯ν μ¬μ©μ μ¬μ μ 보μ₯ν©λλ€. λ§μ°¬κ°μ§λ‘ λ―Έλμ΄ νλ μ΄μ΄μ κ²½μ°, refλ₯Ό μ¬μ©νλ©΄ HTML5 <video> λλ <audio> μμμ λ€μ΄ν°λΈ APIμ μ§μ μνΈμμ©νλ μ¬μ©μ μ μ 컨νΈλ‘€(μ¬μ, μΌμ μ€μ§, λ³Όλ₯¨, νμ)μ ꡬμΆνμ¬ λΈλΌμ°μ κΈ°λ³Έκ°κ³Ό λ
립μ μΈ μΌκ΄λ κ²½νμ μ 곡ν μ μμ΅λλ€.
2. λͺ λ Ήν μ λλ©μ΄μ λ° μΊλ²μ€ μνΈμμ© νΈλ¦¬κ±°
μ μΈμ μ λλ©μ΄μ λΌμ΄λΈλ¬λ¦¬λ λ§μ UI ν¨κ³Όμ νλ₯νμ§λ§, νΉν HTML5 μΊλ²μ€ API, WebGLμ νμ©νκ±°λ Reactμ λ λλ§ μ£ΌκΈ° μΈλΆμμ κ΄λ¦¬νλ κ²μ΄ κ°μ₯ μ’μ μμ μμ±μ λν μΈλ°ν μ μ΄κ° νμν μΌλΆ κ³ κΈ μ λλ©μ΄μ μ refλ‘λΆν° ν° μ΄μ μ μ»μ΅λλ€. μλ₯Ό λ€μ΄, μΊλ²μ€ μμμ μ€μκ° λ°μ΄ν° μκ°ν λλ κ²μμ λ§λλ κ²μ λ³Έμ§μ μΌλ‘ λͺ λ Ήν νλ‘μΈμ€μΈ ν½μ λ²νΌμ μ§μ 그리λ κ²μ ν¬ν¨ν©λλ€.
import React from 'react';
class CanvasAnimator extends React.Component {
constructor(props) {
super(props);
this.canvasRef = React.createRef();
this.animationFrameId = null;
}
componentDidMount() {
this.startAnimation();
}
componentWillUnmount() {
this.stopAnimation();
}
startAnimation = () => {
const canvas = this.canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
let angle = 0;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 50;
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height); // μΊλ²μ€ μ§μ°κΈ°
// νμ νλ μ¬κ°ν 그리기
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(angle);
ctx.fillStyle = '#6f42c1';
ctx.fillRect(-radius / 2, -radius / 2, radius, radius);
ctx.restore();
angle += 0.05; // νμ μ μν΄ κ°λ μ¦κ°
this.animationFrameId = requestAnimationFrame(animate);
};
this.animationFrameId = requestAnimationFrame(animate);
};
stopAnimation = () => {
if (this.animationFrameId) {
cancelAnimationFrame(this.animationFrameId);
}
};
render() {
return (
<div style={{ textAlign: 'center', margin: '30px auto', border: '1px solid #ced4da', padding: '20px', borderRadius: '8px', background: '#f8f9fa' }}>
<h3>createRefλ₯Ό μ¬μ©ν λͺ
λ Ήν μΊλ²μ€ μ λλ©μ΄μ
</h3>
<p>μ΄ μΊλ²μ€ μ λλ©μ΄μ
μ refλ₯Ό ν΅ν΄ λΈλΌμ°μ APIλ₯Ό μ¬μ©νμ¬ μ§μ μ μ΄λ©λλ€.</p>
<canvas ref={this.canvasRef} width="300" height="200" style={{ border: '1px solid #adb5bd', background: 'white' }}>
μ¬μ© μ€μΈ λΈλΌμ°μ λ HTML5 μΊλ²μ€ νκ·Έλ₯Ό μ§μνμ§ μμ΅λλ€.
</canvas>
<p><em>μ΄λ¬ν μ§μ μ μΈ μ μ΄λ κ³ μ±λ₯ κ·Έλν½, κ²μ λλ μ μΈκ³ λ€μν μ°μ
μμ μ¬μ©λλ μ λ¬Έ λ°μ΄ν° μκ°νμ νμμ μ
λλ€.</em></p>
</div>
);
}
}
μ΄ μ»΄ν¬λνΈλ μΊλ²μ€ μμλ₯Ό μ 곡νκ³ refλ₯Ό μ¬μ©νμ¬ 2D λ λλ§ μ»¨ν μ€νΈμ μ§μ μ κ·Όν©λλ€. `requestAnimationFrame`μ μν΄ κ΅¬λλλ μ λλ©μ΄μ 루νλ νμ νλ μ¬κ°νμ λͺ λ Ήμ μΌλ‘ κ·Έλ¦¬κ³ μ λ°μ΄νΈν©λλ€. μ΄ ν¨ν΄μ μ¬μ©μμ μ§λ¦¬μ μμΉλ μ₯μΉ κΈ°λ₯μ κ΄κ³μμ΄ μ λ°ν νλ μλ³ λ λλ§μ μꡬνλ λνν λ°μ΄ν° λμ보λ, μ¨λΌμΈ λμμΈ λꡬ λλ μΊμ£ΌμΌ κ²μμ ꡬμΆνλ λ° κΈ°λ³Έμ΄ λ©λλ€.
3. μλνν° DOM λΌμ΄λΈλ¬λ¦¬μμ ν΅ν©: μνν μ°κ²°
refλ₯Ό μ¬μ©νλ κ°μ₯ κ°λ ₯ν μ΄μ μ€ νλλ Reactλ₯Ό DOMμ μ§μ μ‘°μνλ μΈλΆ μλ°μ€ν¬λ¦½νΈ λΌμ΄λΈλ¬λ¦¬μ ν΅ν©νλ κ²μ λλ€. λ§μ κ°λ ₯ν λΌμ΄λΈλ¬λ¦¬, νΉν μ€λλμκ±°λ νΉμ λ λλ§ μμ (μ°¨νΈ, λ§€ν λλ λ¦¬μΉ ν μ€νΈ νΈμ§κ³Ό κ°μ)μ μ€μ μ λ λΌμ΄λΈλ¬λ¦¬λ DOM μμλ₯Ό λμμΌλ‘ μΌκ³ κ·Έ λ΄μ©μ μ§μ κ΄λ¦¬νλ λ°©μμΌλ‘ μλν©λλ€. Reactλ μ μΈμ λͺ¨λμμ λμΌν DOM νμ νΈλ¦¬λ₯Ό μ μ΄νλ €κ³ μλν¨μΌλ‘μ¨ μ΄λ¬ν λΌμ΄λΈλ¬λ¦¬μ μΆ©λν μ μμ΅λλ€. Refλ μΈλΆ λΌμ΄λΈλ¬λ¦¬λ₯Ό μν μ§μ λ '컨ν μ΄λ'λ₯Ό μ 곡ν¨μΌλ‘μ¨ μ΄ μΆ©λμ λ°©μ§ν©λλ€.
import React from 'react';
import * as d3 from 'd3'; // D3.jsκ° μ€μΉλκ³ import λμλ€κ³ κ°μ
class D3BarChart extends React.Component {
constructor(props) {
super(props);
this.chartContainerRef = React.createRef();
}
// μ»΄ν¬λνΈκ° λ§μ΄νΈλ λ μ°¨νΈ κ·Έλ¦¬κΈ°
componentDidMount() {
this.drawChart();
}
// μ»΄ν¬λνΈκ° μ
λ°μ΄νΈλ λ(μ: props.data λ³κ²½) μ°¨νΈ μ
λ°μ΄νΈ
componentDidUpdate(prevProps) {
if (prevProps.data !== this.props.data) {
this.drawChart();
}
}
// μ»΄ν¬λνΈκ° μΈλ§μ΄νΈλ λ λ©λͺ¨λ¦¬ λμ λ°©μ§λ₯Ό μν΄ D3 μμ μ 리
componentWillUnmount() {
d3.select(this.chartContainerRef.current).selectAll('*').remove();
}
drawChart = () => {
const data = this.props.data || [40, 80, 20, 100, 60, 90]; // κΈ°λ³Έ λ°μ΄ν°
const node = this.chartContainerRef.current;
if (!node) return; // refκ° μ¬μ© κ°λ₯νμ§ νμΈ
// μ΄μ μ D3κ° κ·Έλ¦° μ°¨νΈ μμ λͺ¨λ μ§μ°κΈ°
d3.select(node).selectAll('*').remove();
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
const width = 460 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const svg = d3.select(node)
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// μ€μΌμΌ μ€μ
const x = d3.scaleBand()
.range([0, width])
.padding(0.1);
const y = d3.scaleLinear()
.range([height, 0]);
x.domain(data.map((d, i) => i)); // λ¨μνλ₯Ό μν΄ μΈλ±μ€λ₯Ό λλ©μΈμΌλ‘ μ¬μ©
y.domain([0, d3.max(data)]);
// λ§λ μΆκ°
svg.selectAll('.bar')
.data(data)
.enter().append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => x(i))
.attr('width', x.bandwidth())
.attr('y', d => y(d))
.attr('height', d => height - y(d))
.attr('fill', '#17a2b8');
// XμΆ μΆκ°
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x));
// YμΆ μΆκ°
svg.append('g')
.call(d3.axisLeft(y));
};
render() {
return (
<div style={{ textAlign: 'center', margin: '30px auto', border: '1px solid #00a0b2', padding: '20px', borderRadius: '8px', background: '#e0f7fa' }}>
<h3>React createRefλ₯Ό μ¬μ©ν D3.js μ°¨νΈ ν΅ν©</h3>
<p>μ΄ λ°μ΄ν° μκ°νλ Reactκ° κ΄λ¦¬νλ 컨ν
μ΄λ λ΄μμ D3.jsμ μν΄ λ λλ§λ©λλ€.</p>
<div ref={this.chartContainerRef} /> // D3.jsκ° μ΄ div μμ λ λλ§ν κ²μ
λλ€.
<p><em>μ΄λ¬ν μ λ¬Έ λΌμ΄λΈλ¬λ¦¬λ₯Ό ν΅ν©νλ κ²μ λ°μ΄ν°κ° λ§μ μ ν리μΌμ΄μ
μ μ€μνλ©°, λ€μν μ°μ
λ° μ§μμ μ¬μ©μμκ² κ°λ ₯ν λΆμ λꡬλ₯Ό μ 곡ν©λλ€.</em></p>
</div>
);
}
}
μ΄ κ΄λ²μν μμ λ React ν΄λμ€ μ»΄ν¬λνΈ λ΄μ D3.js λ§λ μ°¨νΈλ₯Ό ν΅ν©νλ κ²μ 보μ¬μ€λλ€. chartContainerRefλ D3.jsκ° λ λλ§μ μννλ λ° νμν νΉμ DOM λ
Έλλ₯Ό μ 곡ν©λλ€. Reactλ 컨ν
μ΄λ <div>μ μλͺ
μ£ΌκΈ°λ₯Ό μ²λ¦¬νκ³ , D3.jsλ κ·Έ λ΄λΆ μ½ν
μΈ λ₯Ό κ΄λ¦¬ν©λλ€. `componentDidUpdate` λ° `componentWillUnmount` λ©μλλ λ°μ΄ν°κ° λ³κ²½λ λ μ°¨νΈλ₯Ό μ
λ°μ΄νΈνκ³ νμν μ 리λ₯Ό μννμ¬ λ©λͺ¨λ¦¬ λμλ₯Ό λ°©μ§νκ³ λ°μμ± μλ κ²½νμ 보μ₯νλ λ° νμμ μ
λλ€. μ΄ ν¨ν΄μ 보νΈμ μΌλ‘ μ μ© κ°λ₯νλ©°, κ°λ°μκ° Reactμ μ»΄ν¬λνΈ λͺ¨λΈκ³Ό μ λ¬Ένλ κ³ μ±λ₯ μκ°ν λΌμ΄λΈλ¬λ¦¬μ μ₯μ μ λͺ¨λ νμ©νμ¬ κΈλ‘λ² λμ보λ λ° λΆμ νλ«νΌμ ꡬμΆν μ μλλ‘ ν©λλ€.
4. λμ λ μ΄μμμ μν μμ ν¬κΈ° λλ μμΉ μΈ‘μ
λ§€μ° λμ μ΄κ±°λ λ°μν λ μ΄μμ, λλ 보μ΄λ νλͺ©λ§ λ λλ§νλ κ°μνλ λͺ©λ‘κ³Ό κ°μ κΈ°λ₯μ ꡬννκΈ° μν΄μλ μμμ μ νν ν¬κΈ°μ μμΉλ₯Ό μλ κ²μ΄ μ€μν©λλ€. Refλ₯Ό μ¬μ©νλ©΄ DOMμμ μ§μ μ΄ μ€μν μ 보λ₯Ό μ 곡νλ getBoundingClientRect() λ©μλμ μ κ·Όν μ μμ΅λλ€.
import React from 'react';
class ElementDimensionLogger extends React.Component {
constructor(props) {
super(props);
this.measurableDivRef = React.createRef();
this.state = {
width: 0,
height: 0,
top: 0,
left: 0,
message: 'μΈ‘μ νλ €λ©΄ λ²νΌμ ν΄λ¦νμΈμ!'
};
}
componentDidMount() {
// μ΄κΈ° μΈ‘μ μ μ’
μ’
μ μ©νμ§λ§, μ¬μ©μ νλμ μν΄ νΈλ¦¬κ±°λ μλ μμ΅λλ€.
this.measureElement();
// λμ λ μ΄μμμ κ²½μ°, μ°½ ν¬κΈ° μ‘°μ μ΄λ²€νΈλ₯Ό μμ ν μ μμ΅λλ€.
window.addEventListener('resize', this.measureElement);
}
componentWillUnmount() {
window.removeEventListener('resize', this.measureElement);
}
measureElement = () => {
if (this.measurableDivRef.current) {
const rect = this.measurableDivRef.current.getBoundingClientRect();
this.setState({
width: Math.round(rect.width),
height: Math.round(rect.height),
top: Math.round(rect.top),
left: Math.round(rect.left),
message: 'ν¬κΈ°κ° μ
λ°μ΄νΈλμμ΅λλ€.'
});
} else {
this.setState({ message: 'μμκ° μμ§ λ λλ§λμ§ μμμ΅λλ€.' });
}
};
render() {
const { width, height, top, left, message } = this.state;
const boxStyle = {
width: '70%',
minHeight: '150px',
border: '3px solid #ffc107',
margin: '25px auto',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
background: '#fff3cd',
borderRadius: '8px',
textAlign: 'center'
};
return (
<div style={{ maxWidth: '700px', margin: '30px auto', padding: '25px', boxShadow: '0 4px 12px rgba(0,0,0,0.08)', borderRadius: '10px', background: 'white' }}>
<h3>createRefλ‘ μμ ν¬κΈ° μΈ‘μ νκΈ°</h3>
<p>μ΄ μμ λ λμ μμμ ν¬κΈ°μ μμΉλ₯Ό λμ μΌλ‘ κ°μ Έμ νμν©λλ€.</p>
<div ref={this.measurableDivRef} style={boxStyle}>
<p><strong>μ κ° μΈ‘μ λμ μμμ
λλ€.</strong></p>
<p>λΈλΌμ°μ μ°½ ν¬κΈ°λ₯Ό μ‘°μ νμ¬ μλ‘κ³ μΉ¨/μλ νΈλ¦¬κ±° μ μΈ‘μ κ°μ΄ λ³κ²½λλ κ²μ νμΈνμΈμ.</p>
</div>
<button
onClick={this.measureElement}
style={{ padding: '10px 20px', background: '#6c757d', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginBottom: '15px' }}
>
μ§κΈ μΈ‘μ νκΈ°
</button>
<div style={{ background: '#f0f0f0', padding: '15px', borderRadius: '6px' }}>
<p><strong>μ€μκ° ν¬κΈ°:</strong></p>
<ul style={{ listStyleType: 'none', padding: 0, textAlign: 'left', margin: '0 auto', maxWidth: '300px' }}>
<li>λλΉ: <b>{width}px</b></li>
<li>λμ΄: <b>{height}px</b></li>
<li>μλ¨ μμΉ (λ·°ν¬νΈ): <b>{top}px</b></li>
<li>μΌμͺ½ μμΉ (λ·°ν¬νΈ): <b>{left}px</b></li>
</ul>
<p><em>μ νν μμ μΈ‘μ μ μ μΈκ³ λ€μν μ₯μΉμμ λ°μν λμμΈκ³Ό μ±λ₯ μ΅μ νμ μ€μν©λλ€.</em></p>
</div>
</div>
);
}
}
μ΄ μ»΄ν¬λνΈλ createRefλ₯Ό μ¬μ©νμ¬ div μμμ getBoundingClientRect()λ₯Ό μ»μ΄ μ€μκ° ν¬κΈ°μ μμΉλ₯Ό μ 곡ν©λλ€. μ΄ μ 보λ 볡μ‘ν λ μ΄μμ μ‘°μ , κ°μνλ μ€ν¬λ‘€ λͺ©λ‘μμμ κ°μμ± κ²°μ , λλ μμκ° νΉμ λ·°ν¬νΈ μμ λ΄μ μλμ§ νμΈνλ λ° λ§€μ° μ μ©ν©λλ€. νλ©΄ ν¬κΈ°, ν΄μλ λ° λΈλΌμ°μ νκ²½μ΄ λ§€μ° λ€μν μ μΈκ³ μ¬μ©μλ₯Ό μν΄, μ€μ DOM μΈ‘μ μ κΈ°λ°μΌλ‘ ν μ λ°ν λ μ΄μμ μ μ΄λ μΌκ΄λκ³ κ³ νμ§μ μ¬μ©μ κ²½νμ μ 곡νλ ν΅μ¬ μμμ
λλ€.
`createRef` μ¬μ©μ μν λͺ¨λ² μ¬λ‘ λ° μ£Όμμ¬ν
createRefλ κ°λ ₯ν λͺ
λ Ήν μ μ΄λ₯Ό μ 곡νμ§λ§, μλͺ» μ¬μ©νλ©΄ κ΄λ¦¬νκ³ λλ²κΉ
νκΈ° μ΄λ €μ΄ μ½λλ‘ μ΄μ΄μ§ μ μμ΅λλ€. κ·Έ νμ μ±
μκ° μκ² νμ©νκΈ° μν΄μλ λͺ¨λ² μ¬λ‘λ₯Ό μ€μνλ κ²μ΄ νμμ μ
λλ€.
1. μ μΈμ μ κ·Ό μ°μ : ν©κΈλ₯
refλ Reactμ μ£Όμ μνΈμμ© λ°©μμ΄ μλ "νμΆκ΅¬"μμ νμ κΈ°μ΅νμΈμ. refλ₯Ό μ¬μ©νκΈ° μ μ μλ¬Έν΄ λ³΄μΈμ: μ΄κ²μ΄ stateμ propsλ‘ λ¬μ±λ μ μλκ°? λ΅μ΄ 'μ'λΌλ©΄, κ·Έκ²μ΄ κ±°μ νμ λ λ«κ³ , λ "Reactμ€λ¬μ΄" μ κ·Ό λ°©μμ
λλ€. μλ₯Ό λ€μ΄, μ
λ ₯ κ°λ₯Ό λ³κ²½νκ³ μΆλ€λ©΄, inputRef.current.valueλ₯Ό μ§μ μ€μ νλ ref λμ stateλ₯Ό μ¬μ©νλ μ μ΄ μ»΄ν¬λνΈλ₯Ό μ¬μ©νμΈμ.
2. Refλ λͺ λ Ήν μνΈμμ©μ μν κ²μ΄μ§, μν κ΄λ¦¬λ₯Ό μν κ²μ΄ μλλ€
Refλ DOM μμλ μ»΄ν¬λνΈ μΈμ€ν΄μ€μ λν μ§μ μ μ΄κ³ λͺ λ Ήμ μΈ μμ μ ν¬ν¨νλ μμ μ κ°μ₯ μ ν©ν©λλ€. κ·Έκ²λ€μ "μ΄ μ λ ₯μ ν¬μ»€μ€ν΄λΌ", "μ΄ λΉλμ€λ₯Ό μ¬μν΄λΌ", "μ΄ μΉμ μΌλ‘ μ€ν¬λ‘€ν΄λΌ"μ κ°μ λͺ λ Ήμ λλ€. stateλ₯Ό κΈ°λ°μΌλ‘ μ»΄ν¬λνΈμ μ μΈμ UIλ₯Ό λ³κ²½νκΈ° μν κ²μ΄ μλλλ€. propsλ stateλ‘ μ μ΄ν μ μλ μμμ μ€νμΌμ΄λ μ½ν μΈ λ₯Ό refλ₯Ό ν΅ν΄ μ§μ μ‘°μνλ©΄ Reactμ κ°μ DOMμ΄ μ€μ DOMκ³Ό λκΈ°νλμ§ μμ μμΈ‘ν μ μλ λμκ³Ό λ λλ§ λ¬Έμ λ₯Ό μΌμΌν¬ μ μμ΅λλ€.
3. Refμ ν¨μν μ»΄ν¬λνΈ: `useRef`μ `forwardRef` μμ©νκΈ°
ν¨μν μ»΄ν¬λνΈ λ΄μ νλμ μΈ React κ°λ°μμλ React.createRef()λ₯Ό μ¬μ©νμ§ μμ΅λλ€. λμ useRef ν
μ μμ‘΄νκ² λ©λλ€. useRef ν
μ createRefμ μ μ¬ν λ³κ²½ κ°λ₯ν ref κ°μ²΄λ₯Ό μ 곡νλ©°, κ·Έ .current μμ±μ λμΌν λͺ
λ Ήν μνΈμμ©μ μ¬μ©λ μ μμ΅λλ€. κ·Έκ²μ μ»΄ν¬λνΈ λ¦¬λ λλ§ μ¬μ΄μ κ°μ μ μ§νλ©΄μλ 리λ λλ§μ μ λ°νμ§ μμΌλ―λ‘, DOM λ
Έλμ λν μ°Έμ‘°λ λ λλ§ μ¬μ΄μ μ§μλμ΄μΌ νλ λ³κ²½ κ°λ₯ν κ°μ 보μ νλ λ° μλ²½ν©λλ€.
import React, { useRef, useEffect } from 'react';
function FunctionalComponentWithRef() {
const myInputRef = useRef(null); // nullλ‘ μ΄κΈ°ν
useEffect(() => {
// μ΄ μ½λλ μ»΄ν¬λνΈκ° λ§μ΄νΈλ ν μ€νλ©λλ€.
if (myInputRef.current) {
myInputRef.current.focus();
console.log('ν¨μν μ»΄ν¬λνΈ μ
λ ₯μ°½μ ν¬μ»€μ€λ¨!');
}
}, []); // λΉ μμ‘΄μ± λ°°μ΄μ λ§μ΄νΈ μ ν λ²λ§ μ€νλλλ‘ λ³΄μ₯ν©λλ€.
const handleLogValue = () => {
if (myInputRef.current) {
alert(`μ
λ ₯κ°: ${myInputRef.current.value}`);
}
};
return (
<div style={{ margin: '20px', padding: '20px', border: '1px solid #009688', borderRadius: '8px', background: '#e0f2f1' }}>
<h3>ν¨μν μ»΄ν¬λνΈμμ useRef μ¬μ©νκΈ°</h3>
<label htmlFor="funcInput">무μμ΄λ μ
λ ₯νμΈμ:</label><br />
<input id="funcInput" type="text" ref={myInputRef} placeholder="μλμΌλ‘ ν¬μ»€μ€λ©λλ€!" style={{ padding: '8px', margin: '10px 0', borderRadius: '4px', border: '1px solid #ccc' }} /><br />
<button onClick={handleLogValue} style={{ padding: '10px 15px', background: '#009688', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}>
μ
λ ₯κ° κΈ°λ‘νκΈ°
</button>
<p><em>μλ‘μ΄ νλ‘μ νΈμ κ²½μ°, ν¨μν μ»΄ν¬λνΈμμ refλ₯Ό μ¬μ©ν λλ `useRef`κ° κ΄μ©μ μΈ μ νμ
λλ€.</em></p>
</div>
);
}
λΆλͺ¨ μ»΄ν¬λνΈκ° ν¨μν μμ μ»΄ν¬λνΈ λ΄λΆμ DOM μμμ λν refλ₯Ό μ»μ΄μΌ νλ€λ©΄, React.forwardRefκ° ν΄κ²°μ±
μ
λλ€. μ΄κ²μ λΆλͺ¨λ‘λΆν° μμμ DOM μμ μ€ νλλ‘ refλ₯Ό "μ λ¬"ν μ μκ² ν΄μ£Όλ κ³ μ°¨ μ»΄ν¬λνΈλ‘, ν¨μν μ»΄ν¬λνΈμ μΊ‘μνλ₯Ό μ μ§νλ©΄μλ νμν λ λͺ
λ Ήν μ κ·Όμ κ°λ₯νκ² ν©λλ€.
import React, { useRef, useEffect } from 'react';
// λ€μ΄ν°λΈ input μμλ‘ refλ₯Ό λͺ
μμ μΌλ‘ μ λ¬νλ ν¨μν μ»΄ν¬λνΈ
const ForwardedInput = React.forwardRef((props, ref) => (
<input type="text" ref={ref} className="forwarded-input" placeholder={props.placeholder} style={{ padding: '10px', margin: '8px 0', border: '1px solid #ccc', borderRadius: '4px', width: '100%' }} />
));
class ParentComponentUsingForwardRef extends React.Component {
constructor(props) {
super(props);
this.parentInputRef = React.createRef();
}
componentDidMount() {
if (this.parentInputRef.current) {
this.parentInputRef.current.focus();
console.log('μ λ¬λ refλ₯Ό ν΅ν΄ λΆλͺ¨(ν΄λμ€ μ»΄ν¬λνΈ)λ‘λΆν° ν¨μν μ»΄ν¬λνΈ λ΄λΆμ μ
λ ₯μ°½μ ν¬μ»€μ€λ¨!');
}
}
render() {
return (
<div style={{ margin: '20px', padding: '20px', border: '1px solid #6f42c1', borderRadius: '8px', background: '#f5eef9' }}>
<h3>createRefλ₯Ό μ¬μ©ν Ref μ λ¬ μμ (λΆλͺ¨ ν΄λμ€ μ»΄ν¬λνΈ)</h3>
<label>μμΈ μ 보 μ
λ ₯:</label>
<ForwardedInput ref={this.parentInputRef} placeholder="μ΄ μ
λ ₯μ°½μ ν¨μν μ»΄ν¬λνΈ λ΄λΆμ μμ΅λλ€" />
<p><em>μ΄ ν¨ν΄μ μ§μ μ μΈ DOM μ κ·Όμ λ
ΈμΆν΄μΌ νλ μ¬μ¬μ© κ°λ₯ν μ»΄ν¬λνΈ λΌμ΄λΈλ¬λ¦¬λ₯Ό λ§λλ λ° μ€μν©λλ€.</em></p>
</div>
);
}
}
μ΄κ²μ createRefλ₯Ό μ¬μ©νλ ν΄λμ€ μ»΄ν¬λνΈκ° forwardRefλ₯Ό νμ©νμ¬ ν¨μν μ»΄ν¬λνΈ λ΄μ μ€μ²©λ DOM μμμ ν¨κ³Όμ μΌλ‘ μνΈμμ©νλ λ°©λ²μ 보μ¬μ€λλ€. μ΄λ₯Ό ν΅ν΄ ν¨μν μ»΄ν¬λνΈλ νμν λ λͺ
λ Ήν μνΈμμ©μ λλ±νκ² μ°Έμ¬ν μ μκ² λμ΄, νλμ μΈ React μ½λλ² μ΄μ€κ° μ¬μ ν refμ μ΄μ μ λ릴 μ μλλ‘ λ³΄μ₯ν©λλ€.
4. Refλ₯Ό μ¬μ©νμ§ λ§μμΌ ν λ: Reactμ λ¬΄κ²°μ± μ μ§
- μμ μ»΄ν¬λνΈ μν μ μ΄λ₯Ό μν΄: μμ μ»΄ν¬λνΈμ stateλ₯Ό μ§μ μ½κ±°λ μ λ°μ΄νΈνκΈ° μν΄ refλ₯Ό μ λ μ¬μ©νμ§ λ§μΈμ. μ΄λ Reactμ μν κ΄λ¦¬λ₯Ό μ°ννμ¬ μ ν리μΌμ΄μ μ μμΈ‘ λΆκ°λ₯νκ² λ§λλλ€. λμ , stateλ₯Ό propsλ‘ μ λ¬νκ³ , μμμ΄ λΆλͺ¨μκ² μν λ³κ²½μ μμ²ν μ μλλ‘ μ½λ°±μ μ¬μ©νμΈμ.
- propsμ λ체μ¬λ‘μ: refλ₯Ό ν΅ν΄ μμ ν΄λμ€ μ»΄ν¬λνΈμ λ©μλλ₯Ό νΈμΆν μ μμ§λ§, μ΄λ²€νΈ νΈλ€λ¬λ₯Ό μμμκ² propμΌλ‘ μ λ¬νλ κ²μ΄ λ "Reactμ€λ¬μ΄" λ°©μμΌλ‘ λμΌν λͺ©νλ₯Ό λ¬μ±ν μ μλμ§ κ³ λ €ν΄ λ³΄μΈμ. Propsλ λͺ νν λ°μ΄ν° νλ¦μ μ΄μ§νκ³ μ»΄ν¬λνΈ μνΈμμ©μ ν¬λͺ νκ² λ§λλλ€.
-
Reactκ° μ²λ¦¬ν μ μλ κ°λ¨ν DOM μ‘°μμ μν΄: stateμ λ°λΌ μμμ ν
μ€νΈ, μ€νμΌμ λ³κ²½νκ±°λ ν΄λμ€λ₯Ό μΆκ°/μ κ±°νλ €λ©΄ μ μΈμ μΌλ‘ νμΈμ. μλ₯Ό λ€μ΄,
activeν΄λμ€λ₯Ό ν κΈνλ €λ©΄ JSXμμ 쑰건λΆλ‘ μ μ©νμΈμ:<div className={isActive ? 'active' : ''}>,divRef.current.classList.add('active')λμ .
5. μ±λ₯ κ³ λ €μ¬ν λ° κΈλ‘λ² λλ¬ λ²μ
createRef μ체λ μ±λ₯μ΄ μ’μ§λ§, currentλ₯Ό μ¬μ©νμ¬ μνλλ μμ
μ μλΉν μ±λ₯ μν₯μ λ―ΈμΉ μ μμ΅λλ€. μ μ¬μ μ₯μΉλ λλ¦° λ€νΈμν¬ μ°κ²°(μ μΈκ³ λ§μ μ§μμμ μΌλ°μ μ)μ μ¬μ©νλ μ¬μ©μμ κ²½μ°, λΉν¨μ¨μ μΈ DOM μ‘°μμ λ²λ²
κ±°λ¦Ό, λ°μ μλ UI λ° λμ μ¬μ©μ κ²½νμΌλ‘ μ΄μ΄μ§ μ μμ΅λλ€. μ λλ©μ΄μ
, 볡μ‘ν λ μ΄μμ κ³μ° λλ λ¬΄κ±°μ΄ μλνν° λΌμ΄λΈλ¬λ¦¬ ν΅ν©κ³Ό κ°μ μμ
μ refλ₯Ό μ¬μ©ν λ:
-
μ΄λ²€νΈ λλ°μ΄μ€/μ€λ‘ν:
window.resizeλλscrollμ΄λ²€νΈμμ ν¬κΈ°λ₯Ό μΈ‘μ νκΈ° μν΄ refλ₯Ό μ¬μ©νλ κ²½μ°, κ³Όλν ν¨μ νΈμΆκ³Ό DOM μ½κΈ°λ₯Ό λ°©μ§νκΈ° μν΄ μ΄λ¬ν νΈλ€λ¬κ° λλ°μ΄μ€λκ±°λ μ€λ‘νλμλμ§ νμΈνμΈμ. -
DOM μ½κΈ°/μ°κΈ° μΌκ΄ μ²λ¦¬: DOM μ½κΈ° μμ
(μ:
getBoundingClientRect())κ³Ό DOM μ°κΈ° μμ (μ: μ€νμΌ μ€μ )μ νΌν©νμ§ λ§μΈμ. μ΄λ λ μ΄μμ μ€λμ±μ μ λ°ν μ μμ΅λλ€.fastdomκ³Ό κ°μ λꡬλ μ΄λ₯Ό ν¨μ¨μ μΌλ‘ κ΄λ¦¬νλ λ° λμμ΄ λ μ μμ΅λλ€. -
μ€μνμ§ μμ μμ
μ§μ°: μ λλ©μ΄μ
μλ
requestAnimationFrameμ, λ μ€μν DOM μ‘°μμλsetTimeout(..., 0)λλrequestIdleCallbackμ μ¬μ©νμ¬ λ©μΈ μ€λ λλ₯Ό μ°¨λ¨νκ³ λ°μμ±μ μν₯μ λ―ΈμΉμ§ μλλ‘ νμΈμ. - νλͺ ν μ ν: λλ‘λ μλνν° λΌμ΄λΈλ¬λ¦¬μ μ±λ₯μ΄ λ³λͺ© νμμ΄ λ μ μμ΅λλ€. λμμ νκ°νκ±°λ λλ¦° μ°κ²°μ μ¬μ©νλ μ¬μ©μλ₯Ό μν΄ μ΄λ¬ν μ»΄ν¬λνΈλ₯Ό μ§μ° λ‘λ©νλ κ²μ κ³ λ €νμ¬ κΈ°λ³Έ κ²½νμ΄ μ μΈκ³μ μΌλ‘ μ±λ₯μ μ μ§νλλ‘ λ³΄μ₯νμΈμ.
`createRef` vs. μ½λ°± Ref vs. `useRef`: μμΈ λΉκ΅
Reactλ μ§ν κ³Όμ μμ refλ₯Ό μ²λ¦¬νλ λ€μν λ°©λ²μ μ 곡ν΄μμ΅λλ€. κ°κ°μ λ―Έλ¬ν μ°¨μ΄λ₯Ό μ΄ν΄νλ κ²μ νΉμ μν©μ κ°μ₯ μ ν©ν λ°©λ²μ μ ννλ λ° μ€μν©λλ€.
1. `React.createRef()` (ν΄λμ€ μ»΄ν¬λνΈ - νλμ )
-
λ©μ»€λμ¦: μ»΄ν¬λνΈ μΈμ€ν΄μ€μ μμ±μμμ ref κ°μ²΄(
{ current: null })λ₯Ό μμ±ν©λλ€. Reactλ λ§μ΄ν ν DOM μμλ μ»΄ν¬λνΈ μΈμ€ν΄μ€λ₯Ό.currentμμ±μ ν λΉν©λλ€. - μ£Όμ μ¬μ©μ²: ν΄λμ€ μ»΄ν¬λνΈ λ΄μμλ§ λ μ μ μΌλ‘ μ¬μ©λ©λλ€. μ»΄ν¬λνΈ μΈμ€ν΄μ€λΉ ν λ² μ΄κΈ°νλ©λλ€.
-
Ref μ±μ°κΈ°:
.currentλ μ»΄ν¬λνΈκ° λ§μ΄νΈλ ν μμ/μΈμ€ν΄μ€λ‘ μ€μ λκ³ , μΈλ§μ΄νΈ μnullλ‘ μ¬μ€μ λ©λλ€. - μ΅μ μ μ©λ: DOM μμλ μμ ν΄λμ€ μ»΄ν¬λνΈ μΈμ€ν΄μ€λ₯Ό μ°Έμ‘°ν΄μΌ νλ ν΄λμ€ μ»΄ν¬λνΈμ λͺ¨λ νμ€ ref μꡬ μ¬ν.
- μ₯μ : λͺ ννκ³ μ§κ΄μ μΈ κ°μ²΄ μ§ν₯ ꡬ문. μΈλΌμΈ ν¨μ μ¬μμ±μΌλ‘ μΈν μΆκ° νΈμΆμ λν κ±±μ μ΄ μμ΅λλ€ (μ½λ°± refμμ λ°μν μ μλ λ¬Έμ ).
- λ¨μ : ν¨μν μ»΄ν¬λνΈμ ν¨κ» μ¬μ©ν μ μμ΅λλ€. μμ±μμμ μ΄κΈ°νλμ§ μμΌλ©΄(μ: renderμμ), λ λλ§λ§λ€ μ ref κ°μ²΄κ° μμ±λμ΄ μ μ¬μ μΈ μ±λ₯ λ¬Έμ λ μλͺ»λ ref κ°μΌλ‘ μ΄μ΄μ§ μ μμ΅λλ€. μΈμ€ν΄μ€ μμ±μ ν λΉν΄μΌ νλ€λ κ²μ κΈ°μ΅ν΄μΌ ν©λλ€.
2. μ½λ°± Ref (ν΄λμ€ & ν¨μν μ»΄ν¬λνΈ - μ μ°ν¨/λ κ±°μ)
-
λ©μ»€λμ¦:
refpropμ μ§μ ν¨μλ₯Ό μ λ¬ν©λλ€. Reactλ λ§μ΄νΈλ DOM μμλ μ»΄ν¬λνΈ μΈμ€ν΄μ€λ‘ μ΄ ν¨μλ₯Ό νΈμΆνκ³ , λμ€μ μΈλ§μ΄νΈλ λnullλ‘ λ€μ νΈμΆν©λλ€. -
μ£Όμ μ¬μ©μ²: ν΄λμ€μ ν¨μν μ»΄ν¬λνΈ λͺ¨λμμ μ¬μ©ν μ μμ΅λλ€. ν΄λμ€ μ»΄ν¬λνΈμμλ μ½λ°±μ΄ 보ν΅
thisμ λ°μΈλ©λκ±°λ νμ΄ν ν¨μ ν΄λμ€ μμ±μΌλ‘ μ μλ©λλ€. ν¨μν μ»΄ν¬λνΈμμλ μ’ μ’ μΈλΌμΈμΌλ‘ μ μλκ±°λ λ©λͺ¨μ΄μ μ΄μ λ©λλ€. -
Ref μ±μ°κΈ°: μ½λ°± ν¨μλ Reactμ μν΄ μ§μ νΈμΆλ©λλ€. μ°Έμ‘°λ₯Ό μ μ₯νλ κ²μ λΉμ μ μ±
μμ
λλ€ (μ:
this.myInput = element;). -
μ΅μ μ μ©λ: refκ° μ€μ λκ³ ν΄μ λ λ λ μΈλ°ν μ μ΄κ° νμν μλ리μ€, λλ λμ ref λͺ©λ‘κ³Ό κ°μ κ³ κΈ ν¨ν΄μ μ¬μ©λ©λλ€.
createRefμuseRefμ΄μ μ μ£Όμ ref κ΄λ¦¬ λ°©μμ΄μμ΅λλ€. - μ₯μ : μ΅λμ μ μ°μ±μ μ 곡ν©λλ€. refκ° μ¬μ© κ°λ₯ν λ μ¦μ μ κ·Όν μ μμ΅λλ€ (μ½λ°± ν¨μ λ΄μμ). λμ μμ 컬λ μ μ μν΄ λ°°μ΄μ΄λ λ§΅μ refλ₯Ό μ μ₯νλ λ° μ¬μ©ν μ μμ΅λλ€.
-
λ¨μ : μ½λ°±μ΄
renderλ©μλ λ΄μμ μΈλΌμΈμΌλ‘ μ μλλ©΄(μ:ref={el => this.myRef = el}), μ λ°μ΄νΈ μ€μ λ λ² νΈμΆλ©λλ€ (ν λ²μnullλ‘, κ·Έ λ€μμ μμλ‘), μ΄λ μ μ€νκ² μ²λ¦¬νμ§ μμΌλ©΄ μ±λ₯ λ¬Έμ λ μκΈ°μΉ μμ λΆμμ©μ μΌμΌν¬ μ μμ΅λλ€ (μ: μ½λ°±μ ν΄λμ€ λ©μλλ‘ λ§λ€κ±°λ ν¨μν μ»΄ν¬λνΈμμuseCallbackμ¬μ©).
class CallbackRefDetailedExample extends React.Component {
constructor(props) {
super(props);
this.inputElement = null;
}
// μ΄ λ©μλλ Reactμ μν΄ refλ₯Ό μ€μ νκΈ° μν΄ νΈμΆλ©λλ€.
setInputElementRef = element => {
if (element) {
console.log('Ref μμλ:', element);
}
this.inputElement = element; // μ€μ DOM μμλ₯Ό μ μ₯
};
componentDidMount() {
if (this.inputElement) {
this.inputElement.focus();
}
}
render() {
return (
<div>
<label>μ½λ°± Ref μ
λ ₯:</label>
<input type="text" ref={this.setInputElementRef} />
</div>
);
}
}
3. `useRef` ν (ν¨μν μ»΄ν¬λνΈ - νλμ )
-
λ©μ»€λμ¦: λ³κ²½ κ°λ₯ν ref κ°μ²΄(
{ current: initialValue })λ₯Ό λ°ννλ React ν μ λλ€. λ°νλ κ°μ²΄λ ν¨μν μ»΄ν¬λνΈμ μ 체 μλͺ μ£ΌκΈ° λμ μ§μλ©λλ€. - μ£Όμ μ¬μ©μ²: ν¨μν μ»΄ν¬λνΈ λ΄μμλ§ λ μ μ μΌλ‘ μ¬μ©λ©λλ€.
-
Ref μ±μ°κΈ°:
createRefμ μ μ¬νκ², Reactλ λ§μ΄ν ν DOM μμλ μ»΄ν¬λνΈ μΈμ€ν΄μ€(μ λ¬λ κ²½μ°)λ₯Ό.currentμμ±μ ν λΉνκ³ μΈλ§μ΄νΈ μnullλ‘ μ€μ ν©λλ€..currentκ°μ μλμΌλ‘ μ λ°μ΄νΈν μλ μμ΅λλ€. - μ΅μ μ μ©λ: ν¨μν μ»΄ν¬λνΈμ λͺ¨λ ref κ΄λ¦¬. λν 리λ λλ§μ νΈλ¦¬κ±°νμ§ μκ³ λ λλ§ μ¬μ΄μ μ§μλμ΄μΌ νλ λ³κ²½ κ°λ₯ν κ°μ 보μ νλ λ° μ μ©ν©λλ€ (μ: νμ΄λ¨Έ ID, μ΄μ κ°).
- μ₯μ : ν μ λν κ°λ¨νκ³ κ΄μ©μ μΈ λ°©λ². ref κ°μ²΄λ λ λλ§ μ¬μ΄μ μ§μλμ΄ μ¬μμ± λ¬Έμ λ₯Ό νΌν©λλ€. DOM λ ΈλλΏλ§ μλλΌ λͺ¨λ λ³κ²½ κ°λ₯ν κ°μ μ μ₯ν μ μμ΅λλ€.
-
λ¨μ : ν¨μν μ»΄ν¬λνΈ λ΄μμλ§ μλν©λλ€. μλͺ
μ£ΌκΈ° κ΄λ ¨ ref μνΈμμ©(λ§μ΄νΈ μ ν¬μ»€μ± λ±)μλ λͺ
μμ μΈ
useEffectκ° νμν©λλ€.
μμ½νλ©΄:
-
ν΄λμ€ μ»΄ν¬λνΈλ₯Ό μμ±νκ³ refκ° νμνλ€λ©΄,
React.createRef()κ° κΆμ₯λκ³ κ°μ₯ λͺ νν μ νμ λλ€. -
ν¨μν μ»΄ν¬λνΈλ₯Ό μμ±νκ³ refκ° νμνλ€λ©΄,
useRefν μ΄ νλμ μ΄κ³ κ΄μ©μ μΈ ν΄κ²°μ± μ λλ€. - μ½λ°± refλ μ¬μ ν μ ν¨νμ§λ§ μΌλ°μ μΌλ‘ λ μ₯ν©νκ³ μ μ€νκ² κ΅¬ννμ§ μμΌλ©΄ λ―Έλ¬ν λ¬Έμ μ μ·¨μ½ν©λλ€. κ³ κΈ μλ리μ€λ ν μ μ¬μ©ν μ μλ μ€λλ μ½λλ² μ΄μ€ λλ 컨ν μ€νΈμμ μμ ν λ μ μ©ν©λλ€.
-
μ»΄ν¬λνΈλ₯Ό ν΅ν΄ refλ₯Ό μ λ¬νλ κ²½μ°(νΉν ν¨μν μ»΄ν¬λνΈ),
React.forwardRef()κ° νμμ μ΄λ©°, μ’ μ’ λΆλͺ¨ μ»΄ν¬λνΈμμcreateRefλλuseRefμ ν¨κ» μ¬μ©λ©λλ€.
κΈλ‘λ² κ³ λ €μ¬ν λ° Refλ₯Ό μ¬μ©ν κ³ κΈ μ κ·Όμ±
μ’ μ’ κΈ°μ μ μΈ κ³΅λ°± μνμμ λ Όμλμ§λ§, κΈλ‘λ² λ§μΈλλ₯Ό κ°μ§ μ ν리μΌμ΄μ 컨ν μ€νΈμμ refλ₯Ό μ¬μ©νλ κ²μ νΉν λ€μν μ¬μ©μμ μ±λ₯κ³Ό μ κ·Όμ±μ κ΄ν μ€μν μλ―Έλ₯Ό κ°μ§λλ€.
1. λ€μν μ₯μΉ λ° λ€νΈμν¬λ₯Ό μν μ±λ₯ μ΅μ ν
createRef μμ²΄κ° λ²λ€ ν¬κΈ°μ λ―ΈμΉλ μν₯μ λ―Έλ―Ένλ©°, React μ½μ΄μ μμ λΆλΆμ
λλ€. κ·Έλ¬λ current μμ±μΌλ‘ μννλ μμ
μ μλΉν μ±λ₯ μν₯μ λ―ΈμΉ μ μμ΅λλ€. μ μ¬μ μ₯μΉλ λλ¦° λ€νΈμν¬ μ°κ²°(μ μΈκ³ λ§μ μ§μμμ μΌλ°μ μ)μ μ¬μ©νλ μ¬μ©μμ κ²½μ°, λΉν¨μ¨μ μΈ DOM μ‘°μμ λ²λ²
κ±°λ¦Ό, λ°μ μλ UI λ° λμ μ¬μ©μ κ²½νμΌλ‘ μ΄μ΄μ§ μ μμ΅λλ€. μ λλ©μ΄μ
, 볡μ‘ν λ μ΄μμ κ³μ° λλ λ¬΄κ±°μ΄ μλνν° λΌμ΄λΈλ¬λ¦¬ ν΅ν©κ³Ό κ°μ μμ
μ refλ₯Ό μ¬μ©ν λ:
-
μ΄λ²€νΈ λλ°μ΄μ€/μ€λ‘ν:
window.resizeλλscrollμ΄λ²€νΈμμ ν¬κΈ°λ₯Ό μΈ‘μ νκΈ° μν΄ refλ₯Ό μ¬μ©νλ κ²½μ°, κ³Όλν ν¨μ νΈμΆκ³Ό DOM μ½κΈ°λ₯Ό λ°©μ§νκΈ° μν΄ μ΄λ¬ν νΈλ€λ¬κ° λλ°μ΄μ€λκ±°λ μ€λ‘νλμλμ§ νμΈνμΈμ. -
DOM μ½κΈ°/μ°κΈ° μΌκ΄ μ²λ¦¬: DOM μ½κΈ° μμ
(μ:
getBoundingClientRect())κ³Ό DOM μ°κΈ° μμ (μ: μ€νμΌ μ€μ )μ νΌν©νμ§ λ§μΈμ. μ΄λ λ μ΄μμ μ€λμ±μ μ λ°ν μ μμ΅λλ€.fastdomκ³Ό κ°μ λꡬλ μ΄λ₯Ό ν¨μ¨μ μΌλ‘ κ΄λ¦¬νλ λ° λμμ΄ λ μ μμ΅λλ€. -
μ€μνμ§ μμ μμ
μ§μ°: μ λλ©μ΄μ
μλ
requestAnimationFrameμ, λ μ€μν DOM μ‘°μμλsetTimeout(..., 0)λλrequestIdleCallbackμ μ¬μ©νμ¬ λ©μΈ μ€λ λλ₯Ό μ°¨λ¨νκ³ λ°μμ±μ μν₯μ λ―ΈμΉμ§ μλλ‘ νμΈμ. - νλͺ ν μ ν: λλ‘λ μλνν° λΌμ΄λΈλ¬λ¦¬μ μ±λ₯μ΄ λ³λͺ© νμμ΄ λ μ μμ΅λλ€. λμμ νκ°νκ±°λ λλ¦° μ°κ²°μ μ¬μ©νλ μ¬μ©μλ₯Ό μν΄ μ΄λ¬ν μ»΄ν¬λνΈλ₯Ό μ§μ° λ‘λ©νλ κ²μ κ³ λ €νμ¬ κΈ°λ³Έ κ²½νμ΄ μ μΈκ³μ μΌλ‘ μ±λ₯μ μ μ§νλλ‘ λ³΄μ₯νμΈμ.
2. μ κ·Όμ± ν₯μ (ARIA μμ± λ° ν€λ³΄λ νμ)
Refλ νΉν λ€μ΄ν°λΈ λΈλΌμ°μ μ ν΄λΉ νλͺ©μ΄ μλ μ¬μ©μ μ μ UI μ»΄ν¬λνΈλ₯Ό λ§λ€κ±°λ κΈ°λ³Έ λμμ μ¬μ μν λ λ§€μ° μ κ·Όμ± λμ μΉ μ ν리μΌμ΄μ μ ꡬμΆνλ λ° μ€μν μν μ ν©λλ€. μ μΈκ³ μ¬μ©μλ₯Ό μν΄ μΉ μ½ν μΈ μ κ·Όμ± κ°μ΄λλΌμΈ(WCAG) μ€μλ μ’μ κ΄νμΌ λΏλ§ μλλΌ μ’ μ’ λ²μ μꡬ μ¬νμ λλ€. Refλ λ€μμ κ°λ₯νκ² ν©λλ€:
- νλ‘κ·Έλλ° λ°©μμ ν¬μ»€μ€ κ΄λ¦¬: μ λ ₯ νλμμ 보μλ―μ΄, refλ₯Ό μ¬μ©νλ©΄ ν¬μ»€μ€λ₯Ό μ€μ ν μ μμΌλ©°, μ΄λ ν€λ³΄λ μ¬μ©μμ μ€ν¬λ¦° 리λ νμμ μ€μν©λλ€. μ¬κΈ°μλ λͺ¨λ¬, λλ‘λ€μ΄ λ©λ΄ λλ λνν μμ ― λ΄μμ ν¬μ»€μ€λ₯Ό κ΄λ¦¬νλ κ²μ΄ ν¬ν¨λ©λλ€.
-
λμ ARIA μμ±: refλ₯Ό μ¬μ©νμ¬ DOM μμμ ARIA(Accessible Rich Internet Applications) μμ±(μ:
aria-expanded,aria-controls,aria-live)μ λμ μΌλ‘ μΆκ°νκ±°λ μ λ°μ΄νΈν μ μμ΅λλ€. μ΄λ μκ°μ UIλ§μΌλ‘λ μΆλ‘ ν μ μλ μλ―Έ μ 보λ₯Ό 보쑰 κΈ°μ μ μ 곡ν©λλ€.class CollapsibleSection extends React.Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
this.state = { isExpanded: false };
}
toggleExpanded = () => {
this.setState(prevState => ({ isExpanded: !prevState.isExpanded }), () => {
if (this.buttonRef.current) {
// μνμ λ°λΌ ARIA μμ±μ λμ μΌλ‘ μ λ°μ΄νΈ
this.buttonRef.current.setAttribute('aria-expanded', this.state.isExpanded);
}
});
};
componentDidMount() {
if (this.buttonRef.current) {
this.buttonRef.current.setAttribute('aria-controls', `section-${this.props.id}`);
this.buttonRef.current.setAttribute('aria-expanded', this.state.isExpanded);
}
}
render() {
const { id, title, children } = this.props;
const { isExpanded } = this.state;
return (
<div style={{ margin: '20px auto', maxWidth: '600px', border: '1px solid #0056b3', borderRadius: '8px', background: '#e7f0fa', overflow: 'hidden' }}>
<h4>
<button
ref={this.buttonRef} // ARIA μμ±μ μν λ²νΌμ λν Ref
onClick={this.toggleExpanded}
style={{ background: 'none', border: 'none', padding: '15px 20px', width: '100%', textAlign: 'left', cursor: 'pointer', fontSize: '1.2em', color: '#0056b3', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
id={`section-header-${id}`}
>
{title} <span>▼</span>
</button>
</h4>
{isExpanded && (
<div id={`section-${id}`} role="region" aria-labelledby={`section-header-${id}`} style={{ padding: '0 20px 20px', borderTop: '1px solid #a7d9f7' }}>
{children}
</div>
)}
</div>
);
}
} - ν€λ³΄λ μνΈμμ© μ μ΄: μ¬μ©μ μ μ λλ‘λ€μ΄, μ¬λΌμ΄λ λλ κΈ°ν λνν μμμ κ²½μ°, νΉμ ν€λ³΄λ μ΄λ²€νΈ νΈλ€λ¬(μ: λͺ©λ‘ λ΄ νμμ μν νμ΄ν ν€)λ₯Ό ꡬνν΄μΌ ν μ μμ΅λλ€. Refλ μ΄λ¬ν μ΄λ²€νΈ 리μ€λλ₯Ό μ°κ²°νκ³ κ΄λ¦¬ν μ μλ λμ DOM μμμ λν μ κ·Όμ μ 곡ν©λλ€.
refλ₯Ό μ μ€νκ² μ μ©ν¨μΌλ‘μ¨ κ°λ°μλ μ μΈκ³ μ₯μ μΈμ΄ μ ν리μΌμ΄μ μ μ¬μ©νκ³ ν¬μ©ν μ μλλ‘ λ³΄μ₯νμ¬ κΈλ‘λ² λλ¬ λ²μμ μν₯μ ν¬κ² νμ₯ν μ μμ΅λλ€.
3. κ΅μ ν(I18n) λ° νμ§νλ μνΈμμ©
κ΅μ ν(i18n) μμ
μ, refλ λ―Έλ¬νμ§λ§ μ€μν μν μ ν μ μμ΅λλ€. μλ₯Ό λ€μ΄, μ€λ₯Έμͺ½μμ μΌμͺ½(RTL) μ€ν¬λ¦½νΈ(μλμ΄, νλΈλ¦¬μ΄ λλ νλ₯΄μμμ΄ λ±)λ₯Ό μ¬μ©νλ μΈμ΄μμλ μμ°μ€λ¬μ΄ ν μμμ μ€ν¬λ‘€ λ°©ν₯μ΄ μΌμͺ½μμ μ€λ₯Έμͺ½(LTR) μΈμ΄μ λ€λ₯Ό μ μμ΅λλ€. refλ₯Ό μ¬μ©νμ¬ νλ‘κ·Έλλ° λ°©μμΌλ‘ ν¬μ»€μ€λ μ€ν¬λ‘€μ κ΄λ¦¬νλ κ²½μ°, λ‘μ§μ΄ λ¬Έμ λλ μμμ ν
μ€νΈ λ°©ν₯(dir μμ±)μ μ‘΄μ€νλλ‘ λ³΄μ₯νλ κ²μ΄ μ€μν©λλ€.
- RTL μΈμ ν¬μ»€μ€ κ΄λ¦¬: λΈλΌμ°μ λ μΌλ°μ μΌλ‘ RTLμ λν κΈ°λ³Έ ν μμλ₯Ό μ¬λ°λ₯΄κ² μ²λ¦¬νμ§λ§, μ¬μ©μ μ μ ν¬μ»€μ€ νΈλ©μ΄λ μμ°¨μ ν¬μ»€μ±μ ꡬννλ κ²½μ°, μΌκ΄λκ³ μ§κ΄μ μΈ κ²½νμ 보μ₯νκΈ° μν΄ ref κΈ°λ° λ‘μ§μ RTL νκ²½μμ μ² μ ν ν μ€νΈν΄μΌ ν©λλ€.
-
RTLμμμ λ μ΄μμ μΈ‘μ : refλ₯Ό ν΅ν΄
getBoundingClientRect()λ₯Ό μ¬μ©ν λ,leftλ°rightμμ±μ λ·°ν¬νΈμ μλμ μ΄λΌλ μ μ μμλμΈμ. μκ°μ μμ/λμ μμ‘΄νλ λ μ΄μμ κ³μ°μ κ²½μ°,document.dirλλ μμμ κ³μ°λ μ€νμΌμ κ³ λ €νμ¬ RTL λ μ΄μμμ λ§κ² λ‘μ§μ μ‘°μ νμΈμ. - μλνν° λΌμ΄λΈλ¬λ¦¬ ν΅ν©: refλ₯Ό ν΅ν΄ ν΅ν©λ λͺ¨λ μλνν° λΌμ΄λΈλ¬λ¦¬(μ: μ°¨νΈ λΌμ΄λΈλ¬λ¦¬)κ° μ체μ μΌλ‘ i18nμ μΈμνκ³ μ ν리μΌμ΄μ μ΄ μ§μνλ κ²½μ° RTL λ μ΄μμμ μ¬λ°λ₯΄κ² μ²λ¦¬νλμ§ νμΈνμΈμ. μ΄λ₯Ό 보μ₯νλ μ± μμ μ’ μ’ λΌμ΄λΈλ¬λ¦¬λ₯Ό React μ»΄ν¬λνΈμ ν΅ν©νλ κ°λ°μμκ² μμ΅λλ€.
κ²°λ‘ : κΈλ‘λ² μ ν리μΌμ΄μ μ μν `createRef`λ‘ λͺ λ Ήν μ μ΄ λ§μ€ν°νκΈ°
React.createRef()λ Reactμ "νμΆκ΅¬" μ΄μμ
λλ€; κ·Έκ²μ Reactμ κ°λ ₯ν μ μΈμ ν¨λ¬λ€μκ³Ό λΈλΌμ°μ DOM μνΈμμ©μ λͺ
λ Ήν νμ€ μ¬μ΄μ κ°κ·Ήμ λ©μ°λ νμμ μΈ λꡬμ
λλ€. μλ‘μ΄ ν¨μν μ»΄ν¬λνΈμμμ μν μ useRef ν
μ μν΄ λλΆλΆ λ체λμμ§λ§, createRefλ μ¬μ ν μ μΈκ³ λ§μ κΈ°μ
μ ν리μΌμ΄μ
μ μλΉ λΆλΆμ μ°¨μ§νλ ν΄λμ€ μ»΄ν¬λνΈ λ΄μμ refλ₯Ό κ΄λ¦¬νλ νμ€μ μ΄κ³ κ°μ₯ κ΄μ©μ μΈ λ°©λ²μΌλ‘ λ¨μ μμ΅λλ€.
κ·Έ μμ±, μ°κ²° λ° .current μμ±μ μ€μν μν μ μ² μ ν μ΄ν΄ν¨μΌλ‘μ¨ κ°λ°μλ νλ‘κ·Έλλ° λ°©μμ ν¬μ»€μ€ κ΄λ¦¬, μ§μ μ μΈ λ―Έλμ΄ μ μ΄, λ€μν μλνν° λΌμ΄λΈλ¬λ¦¬(D3.js μ°¨νΈλΆν° μ¬μ©μ μ μ λ¦¬μΉ ν
μ€νΈ νΈμ§κΈ°κΉμ§)μμ μνν ν΅ν©, κ·Έλ¦¬κ³ μ λ°ν μμ ν¬κΈ° μΈ‘μ κ³Ό κ°μ κ³Όμ λ₯Ό μμ μκ² ν΄κ²°ν μ μμ΅λλ€. μ΄λ¬ν κΈ°λ₯μ λ¨μ§ κΈ°μ μ μΈ μ±κ³Όκ° μλλΌ, μ μΈκ³ λ€μν μ¬μ©μ, μ₯μΉ λ° λ¬Ένμ λ§₯λ½μ κ±Έμ³ μ±λ₯μ΄ λ°μ΄λκ³ , μ κ·Ό κ°λ₯νλ©°, μ¬μ©μ μΉνμ μΈ μ ν리μΌμ΄μ
μ ꡬμΆνλ λ° κΈ°λ³Έμ΄ λ©λλ€.
μ΄ νμ μ μ€νκ² μ¬μ©ν΄μΌ ν¨μ κΈ°μ΅νμΈμ. νμ Reactμ μ μΈμ stateμ prop μμ€ν
μ λ¨Όμ μ νΈνμΈμ. λͺ
λ Ήν μ μ΄κ° μ§μ μΌλ‘ νμν λ, createRef(ν΄λμ€ μ»΄ν¬λνΈμ©) λλ useRef(ν¨μν μ»΄ν¬λνΈμ©)λ κ·Έκ²μ λ¬μ±νκΈ° μν κ²¬κ³ νκ³ μ μ μλ λ©μ»€λμ¦μ μ 곡ν©λλ€. refλ₯Ό λ§μ€ν°νλ©΄ νλ μΉ κ°λ°μ μμΈμ μΈ κ²½μ°μ 볡μ‘μ±μ μ²λ¦¬ν μ μλ νμ μ»κ² λμ΄, React μ ν리μΌμ΄μ
μ΄ Reactμ μ°μν μ»΄ν¬λνΈ κΈ°λ° μν€ν
μ²μ ν΅μ¬ μ΄μ μ μ μ§νλ©΄μ μ μΈκ³ μ΄λμμλ λ°μ΄λ μ¬μ©μ κ²½νμ μ 곡ν μ μλλ‘ λ³΄μ₯ν©λλ€.
μΆκ° νμ΅ λ° νμ
- React 곡μ λ¬Έμμ Ref: μμ€μμ μ§μ μ΅μ μ 보λ₯Ό μ»μΌλ €λ©΄ <em>https://react.dev/learn/manipulating-the-dom-with-refs</em>λ₯Ό μ°Έμ‘°νμΈμ.
- Reactμ `useRef` ν μ΄ν΄νκΈ°: ν¨μν μ»΄ν¬λνΈ λ±κ°λ¬Όμ λν΄ λ κΉμ΄ μμλ³΄λ €λ©΄ <em>https://react.dev/reference/react/useRef</em>λ₯Ό νμνμΈμ.
- `forwardRef`λ₯Ό μ¬μ©ν Ref μ λ¬: μ»΄ν¬λνΈλ₯Ό ν΅ν΄ refλ₯Ό ν¨κ³Όμ μΌλ‘ μ λ¬νλ λ°©λ²μ λ°°μ°μΈμ: <em>https://react.dev/reference/react/forwardRef</em>
- μΉ μ½ν μΈ μ κ·Όμ± κ°μ΄λλΌμΈ(WCAG): κΈλ‘λ² μΉ κ°λ°μ νμμ μ λλ€: <em>https://www.w3.org/WAI/WCAG22/quickref/</em>
- React μ±λ₯ μ΅μ ν: κ³ μ±λ₯ μ±μ μν λͺ¨λ² μ¬λ‘: <em>https://react.dev/learn/optimizing-performance</em>